【发布时间】:2018-09-04 22:28:07
【问题描述】:
我正在尝试使我的 Spring Boot 1.5.x REST API 项目与 2.x.x 兼容,而不会破坏大量代码。我被困在我曾经使用的基于过滤器的 JWT Spring Security 实现中:
为了通过“/login”端点验证凭据,我曾经扩展UsernamePasswordAuthenticationFilter如下:
public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
private AuthenticationManager authenticationManager;
private JWTUtil jwtUtil;
public JWTAuthenticationFilter(AuthenticationManager authenticationManager, JWTUtil jwtUtil) {
this.authenticationManager = authenticationManager;
this.jwtUtil = jwtUtil;
}
@Override
public Authentication attemptAuthentication(HttpServletRequest req,
HttpServletResponse res) throws AuthenticationException {
try {
CredenciaisDTO creds = new ObjectMapper().readValue(req.getInputStream(), CredenciaisDTO.class);
UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(creds.getEmail(), creds.getSenha(), new ArrayList<>());
Authentication auth = authenticationManager.authenticate(authToken);
return auth;
}
catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
protected void successfulAuthentication(HttpServletRequest req,
HttpServletResponse res,
FilterChain chain,
Authentication auth) throws IOException, ServletException {
String username = ((UserSS) auth.getPrincipal()).getUsername();
String token = jwtUtil.generateToken(username);
res.addHeader("Authorization", "Bearer " + token);
res.addHeader("access-control-expose-headers", "Authorization");
}
}
另外,为了授权 Beared-token 请求,我曾经扩展 BasicAuthenticationFilter 如下:
public class JWTAuthorizationFilter extends BasicAuthenticationFilter {
private JWTUtil jwtUtil;
private UserDetailsService userDetailsService;
public JWTAuthorizationFilter(AuthenticationManager authenticationManager, JWTUtil jwtUtil, UserDetailsService userDetailsService) {
super(authenticationManager);
this.jwtUtil = jwtUtil;
this.userDetailsService = userDetailsService;
}
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain chain) throws IOException, ServletException {
String header = request.getHeader("Authorization");
if (header != null && header.startsWith("Bearer ")) {
UsernamePasswordAuthenticationToken auth = getAuthentication(header.substring(7));
if (auth != null) {
SecurityContextHolder.getContext().setAuthentication(auth);
}
}
chain.doFilter(request, response);
}
private UsernamePasswordAuthenticationToken getAuthentication(String token) {
if (jwtUtil.isValid(token)) {
String username = jwtUtil.getUsername(token);
UserDetails user = userDetailsService.loadUserByUsername(username);
return new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities());
}
return null;
}
}
一切都按预期进行:
使用错误凭据向 /login 请求返回 401 并带有 支持“未经授权/身份验证失败:凭据错误”对象。
未经
BasicAuthenticationFilter授权的承载令牌请求 用于返回带有标准“禁止访问/拒绝访问”对象的 403。
但是,如果我在 Spring Boot 2.0.0 项目中使用该代码,则请求 /login 将返回 403 和空体响应。
This 建议在安全配置类的configure 方法中包含http.exceptionHandling().authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED));。这样我可以获得 401 的 /login 请求,但它仍然返回一个空体响应(而不是那个标准的“未授权”错误对象)。此外,现在所有未经BasicAuthenticationFilter 授权的请求也返回 401 和空体响应(而正确的应该返回 403 和正文内的标准“禁止”错误对象)。
我怎样才能恢复以前的理想行为?
【问题讨论】:
-
再次确认,Spring Boot 2.0.0默认开启csrf保护,与之前的版本不同。如果您没有明确启用或禁用 csrf,那么该行为可能会在您的升级中发生变化。
标签: spring spring-boot spring-security