Spring 安全性 - 一个 REST API - 两个不同的身份验证
Spring security - One REST API - two different authentications
我目前正在为我的大学开发 Web 应用程序。大学给我提供授权服务器。
我正在使用休息 api 为我的 angular 前端提供数据。在后端我使用 spring boot + spring security
我正在使用这个 url: /classification-login 进行登录,所以当用户访问这个 url 时,他被重定向到授权服务器,登录,......,你知道进展如何。如果我理解正确,结果是 JSESSIONID 保存在浏览器 cookie 中,应用程序通过 JSESSIONID 识别用户并从其会话中获取他的用户名。
我需要的是对其他 Web 应用程序使用相同的 REST API,因此用户在完全不同的应用程序中登录,另一个应用程序获取他的访问令牌,然后使用此令牌访问我的 API。问题是我的应用程序只能通过 JSESSIONID 识别用户。所以我的问题是,如何设置 spring 安全性以首先通过 jsessionid 检查用户,如果不存在则通过访问令牌检查。
感谢任何人的回复。
Java代码:
@SpringBootApplication
@EnableOAuth2Sso
@ComponentScan
@ImportResource({"classpath:classification-connector.xml", "classpath:classification-security.xml"})
public class Application extends WebSecurityConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http.logout().and().antMatcher("/**").authorizeRequests()
.antMatchers("/classification-login").authenticated()
.anyRequest().permitAll();
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
yaml 中的配置:
debug: true
security:
user:
password: none
oauth2:
client:
accessTokenUri: https://xxx/oauth/token
userAuthorizationUri: https://xxx/oauth/authorize
clientId: supersecret
clientSecret: supersecret
scope: read
resource:
tokenInfoUri: https://xxx/oauth/check_token
我在这篇文章的帮助下自己找到了答案:http://automateddeveloper.blogspot.cz/2014/03/securing-your-mobile-api-spring-security.html
也许有更好的方法,但这是我的解决方案:
我使用了两个配置文件,第一个用于客户端应用程序和原始 Web 应用程序访问的资源,第二个用于我的登录页面
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
@Configuration
@EnableWebSecurity
@Order(1)
public class ConfigApi extends WebSecurityConfigurerAdapter {
@Override protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/api/**")
.csrf()
.disable()
.authorizeRequests().anyRequest().authenticated().and()
.addFilterBefore(new CustomFilter(), BasicAuthenticationFilter.class );
}
}
第一个配置在以 /api/**
开始的 url 秒内的每个请求之前添加过滤器
import org.springframework.boot.autoconfigure.security.oauth2.client.EnableOAuth2Sso;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@Configuration
@EnableWebSecurity
@EnableOAuth2Sso
@Order(2)
public class ConfigLogin extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf()
.disable()
.authorizeRequests()
.antMatchers("/classification-login").authenticated();
}
}
第二个配置表示 url /classification-login 上的请求需要进行身份验证,但没有添加任何过滤器。这意味着用户将被重定向到授权服务器,他在那里登录并且 spring 安全将在他的 session 上保存身份验证(使用 JSESSIONID)
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.context.SecurityContextImpl;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.OAuth2Request;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.filter.GenericFilterBean;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.*;
public class CustomFilter extends GenericFilterBean {
@Value("${security.oauth2.client.clientId}")
private String clientId;
@Value("${security.oauth2.resource.tokenInfoUri}")
private String checkToken;
@Value("${security.oauth2.client.scope}")
private String scope;
@Override
public void doFilter(
ServletRequest request,
ServletResponse response,
FilterChain chain) throws IOException, ServletException {
try {
final HttpServletRequest httpServletRequest = (HttpServletRequest) request;
final String authorization = httpServletRequest.getHeader("Authorization");
final String token = authorization.replace("Bearer ", "");
//Here I verify the user by token sent in headers (using tokenInfoUri of my authorization server)
final RestTemplate restTemplate = new RestTemplate();
final TokenInfo tokenInfo = restTemplate.getForObject(checkToken + "?token=" + token, TokenInfo.class);
final String userName = tokenInfo.getUserName();
final Set<String> scopes = new HashSet<>();
scopes.add(scope);
final OAuth2Request oAuth2Request = new OAuth2Request(Collections.<String, String>emptyMap(), clientId, null, true, scopes, null, null, null, null);
final List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
final UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(userName, null, authorities);
final OAuth2Authentication oAuth2Authentication = new OAuth2Authentication(oAuth2Request, usernamePasswordAuthenticationToken);
oAuth2Authentication.setAuthenticated(true);
final SecurityContextImpl securityContext = (SecurityContextImpl) SecurityContextHolder.getContext();
securityContext.setAuthentication(oAuth2Authentication);
} catch (Exception ignore) {
System.out.println(ignore);
}
chain.doFilter(request, response);
}
}
这是我的过滤器,基本上我只是在请求中检查 headers,读取令牌并自己通过令牌对用户进行身份验证。 (如果没有token,它会捕获异常并继续[可能以后会做得更好,现在没时间了])
结果:
如果用户使用我的 webapp,他使用 /classification-login 登录,然后他被允许使用 api 因为 spring 安全性将他的身份验证保存在他的 session.
如果有人想在他的应用程序中使用 api,他需要在他的应用程序中使用相同的授权服务器,获取他的令牌并在 headers 中的请求中传递它。
如果有人知道更好的解决方案,请随时发表评论,我在这上面花了太多时间,所以我不打算进一步调查。
我目前正在为我的大学开发 Web 应用程序。大学给我提供授权服务器。
我正在使用休息 api 为我的 angular 前端提供数据。在后端我使用 spring boot + spring security
我正在使用这个 url: /classification-login 进行登录,所以当用户访问这个 url 时,他被重定向到授权服务器,登录,......,你知道进展如何。如果我理解正确,结果是 JSESSIONID 保存在浏览器 cookie 中,应用程序通过 JSESSIONID 识别用户并从其会话中获取他的用户名。
我需要的是对其他 Web 应用程序使用相同的 REST API,因此用户在完全不同的应用程序中登录,另一个应用程序获取他的访问令牌,然后使用此令牌访问我的 API。问题是我的应用程序只能通过 JSESSIONID 识别用户。所以我的问题是,如何设置 spring 安全性以首先通过 jsessionid 检查用户,如果不存在则通过访问令牌检查。
感谢任何人的回复。
Java代码:
@SpringBootApplication
@EnableOAuth2Sso
@ComponentScan
@ImportResource({"classpath:classification-connector.xml", "classpath:classification-security.xml"})
public class Application extends WebSecurityConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http.logout().and().antMatcher("/**").authorizeRequests()
.antMatchers("/classification-login").authenticated()
.anyRequest().permitAll();
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
yaml 中的配置:
debug: true
security:
user:
password: none
oauth2:
client:
accessTokenUri: https://xxx/oauth/token
userAuthorizationUri: https://xxx/oauth/authorize
clientId: supersecret
clientSecret: supersecret
scope: read
resource:
tokenInfoUri: https://xxx/oauth/check_token
我在这篇文章的帮助下自己找到了答案:http://automateddeveloper.blogspot.cz/2014/03/securing-your-mobile-api-spring-security.html
也许有更好的方法,但这是我的解决方案:
我使用了两个配置文件,第一个用于客户端应用程序和原始 Web 应用程序访问的资源,第二个用于我的登录页面
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
@Configuration
@EnableWebSecurity
@Order(1)
public class ConfigApi extends WebSecurityConfigurerAdapter {
@Override protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/api/**")
.csrf()
.disable()
.authorizeRequests().anyRequest().authenticated().and()
.addFilterBefore(new CustomFilter(), BasicAuthenticationFilter.class );
}
}
第一个配置在以 /api/**
开始的 url 秒内的每个请求之前添加过滤器import org.springframework.boot.autoconfigure.security.oauth2.client.EnableOAuth2Sso;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@Configuration
@EnableWebSecurity
@EnableOAuth2Sso
@Order(2)
public class ConfigLogin extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf()
.disable()
.authorizeRequests()
.antMatchers("/classification-login").authenticated();
}
}
第二个配置表示 url /classification-login 上的请求需要进行身份验证,但没有添加任何过滤器。这意味着用户将被重定向到授权服务器,他在那里登录并且 spring 安全将在他的 session 上保存身份验证(使用 JSESSIONID)
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.context.SecurityContextImpl;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.OAuth2Request;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.filter.GenericFilterBean;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.*;
public class CustomFilter extends GenericFilterBean {
@Value("${security.oauth2.client.clientId}")
private String clientId;
@Value("${security.oauth2.resource.tokenInfoUri}")
private String checkToken;
@Value("${security.oauth2.client.scope}")
private String scope;
@Override
public void doFilter(
ServletRequest request,
ServletResponse response,
FilterChain chain) throws IOException, ServletException {
try {
final HttpServletRequest httpServletRequest = (HttpServletRequest) request;
final String authorization = httpServletRequest.getHeader("Authorization");
final String token = authorization.replace("Bearer ", "");
//Here I verify the user by token sent in headers (using tokenInfoUri of my authorization server)
final RestTemplate restTemplate = new RestTemplate();
final TokenInfo tokenInfo = restTemplate.getForObject(checkToken + "?token=" + token, TokenInfo.class);
final String userName = tokenInfo.getUserName();
final Set<String> scopes = new HashSet<>();
scopes.add(scope);
final OAuth2Request oAuth2Request = new OAuth2Request(Collections.<String, String>emptyMap(), clientId, null, true, scopes, null, null, null, null);
final List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
final UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(userName, null, authorities);
final OAuth2Authentication oAuth2Authentication = new OAuth2Authentication(oAuth2Request, usernamePasswordAuthenticationToken);
oAuth2Authentication.setAuthenticated(true);
final SecurityContextImpl securityContext = (SecurityContextImpl) SecurityContextHolder.getContext();
securityContext.setAuthentication(oAuth2Authentication);
} catch (Exception ignore) {
System.out.println(ignore);
}
chain.doFilter(request, response);
}
}
这是我的过滤器,基本上我只是在请求中检查 headers,读取令牌并自己通过令牌对用户进行身份验证。 (如果没有token,它会捕获异常并继续[可能以后会做得更好,现在没时间了])
结果:
如果用户使用我的 webapp,他使用 /classification-login 登录,然后他被允许使用 api 因为 spring 安全性将他的身份验证保存在他的 session.
如果有人想在他的应用程序中使用 api,他需要在他的应用程序中使用相同的授权服务器,获取他的令牌并在 headers 中的请求中传递它。
如果有人知道更好的解决方案,请随时发表评论,我在这上面花了太多时间,所以我不打算进一步调查。