【发布时间】:2015-06-12 18:42:43
【问题描述】:
我正在使用带有 Spring Security、自定义 GenericFilterBean 和 AuthenticationProvider 实现的 Spring 4。除了用于创建新会话的 URL 之外,我主要保护 URL:/v2/session(例如,基于用户名和密码登录并返回要使用的 Auth Token在后续需要认证的请求中)配置如下:
@Configuration
@ComponentScan(basePackages={"com.api.security"})
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private ApiAuthenticationProvider apiAuthenticationProvider;
@Autowired
private AuthTokenHeaderAuthenticationFilter authTokenHeaderAuthenticationFilter;
@Autowired
private AuthenticationEntryPoint apiAuthenticationEntryPoint;
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) {
auth.authenticationProvider(apiAuthenticationProvider);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.addFilterBefore(authTokenHeaderAuthenticationFilter, BasicAuthenticationFilter.class) // Main auth filter
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.authorizeRequests()
.antMatchers(HttpMethod.POST, "/v2/session").permitAll()
.anyRequest().authenticated();
http.exceptionHandling()
.authenticationEntryPoint(apiAuthenticationEntryPoint);
}
}
authTokenHeaderAuthenticationFilter 在每个请求上运行并从请求标头中获取 Token:
/**
* Main Auth Filter. Always sets Security Context if the Auth token Header is not empty
*/
@Component
public class AuthTokenHeaderAuthenticationFilter extends GenericFilterBean {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
final String token = ((HttpServletRequest) request).getHeader(RequestHeaders.AUTH_TOKEN_HEADER);
if (StringUtils.isEmpty(token)) {
chain.doFilter(request, response);
return;
}
try {
AuthenticationToken authRequest = new AuthenticationToken(token);
SecurityContextHolder.getContext().setAuthentication(authRequest);
}
} catch (AuthenticationException failed) {
SecurityContextHolder.clearContext();
return;
}
chain.doFilter(request, response); // continue down the chain
}
}
自定义 apiAuthenticationProvider 将尝试根据标头中提供的令牌对所有请求进行身份验证,如果身份验证不成功 - 抛出 AccessException 并且客户端将收到 HTTP 401 响应:
@Component
public class ApiAuthenticationProvider implements AuthenticationProvider {
@Autowired
private remoteAuthService remoteAuthService;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
AuthenticationToken authRequest = (AuthenticationToken) authentication;
String identity = null;
try {
identity = remoteAuthService.getUserIdentityFromToken(authRequest.getToken());
} catch (AccessException e) {
throw new InvalidAuthTokenException("Cannot get user identity from the token", e);
}
return new AuthenticationToken(identity, authRequest.getToken(), getGrantedAuthorites());
}
}
这对于需要身份验证的请求非常有效。这适用于没有身份验证标头的 /v2/session 请求。但是,对于 /v2/session 请求,在标头(或 cookie 中 - 未在代码示例中显示)具有过期的 Auth 令牌;这有时可能会发生,如果客户端未清除标头或继续发送带有请求的 cookie)安全上下文将被初始化,apiAuthenticationProvider 将抛出异常并以 HTTP 401 响应客户端。
由于 /v2/session 已配置为
http.authorizeRequests()
.antMatchers(HttpMethod.POST, "/v2/session").permitAll()
我希望 Spring Security 在调用 ApiAuthenticationProvider.authenticate() 之前确定这一点。对于配置为 permitAll() 的 URL,过滤器或身份验证提供程序应该如何忽略/不抛出异常?
【问题讨论】:
标签: java spring security authentication spring-security