我试图使我的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使用错误的凭据用于返回带有a的401 standand&#34;未授权/身份验证失败:凭据错误&#34;对象
未经BasicAuthenticationFilter
授权的Bearing-token请求
用于返回403标准&#34;禁止/拒绝访问&#34;对象
但是,如果我在Spring Boot 2.0.0项目中使用该代码,则请求/ login返回403并返回空体响应。
This发帖建议在安全配置类的http.exceptionHandling().authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED));
方法中加入configure
。这样我可以获得401 for / login请求,但它仍然返回一个空体响应(而不是那个标准&#34; Unauthorized&#34;错误对象)。此外,现在所有未经BasicAuthenticationFilter
授权的请求也返回401,其中包含空体响应(正确的应该返回403,使用该标准&#34;禁止&#34;错误对象在体内)。
我怎样才能恢复以前的理想行为?
答案 0 :(得分:3)
知道了。 here的答案没有必要。为了解决我的问题,我实现了AuthenticationFailureHandler
:
public class JWTAuthenticationFailureHandler implements AuthenticationFailureHandler {
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception)
throws IOException, ServletException {
response.setStatus(401);
response.setContentType("application/json");
response.getWriter().append(json());
}
private String json() {
long date = new Date().getTime();
return "{\"timestamp\": " + date + ", "
+ "\"status\": 401, "
+ "\"error\": \"Unauthorized\", "
+ "\"message\": \"Authentication failed: bad credentials\", "
+ "\"path\": \"/login\"}";
}
}
然后将其注入UsernamePasswordAuthenticationFilter
:
public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
(...)
public JWTAuthenticationFilter(AuthenticationManager authenticationManager, JWTUtil jwtUtil) {
super.setAuthenticationFailureHandler(new JWTAuthenticationFailureHandler());
(...)