我已经实现了一些简单的方法来在 springboot 中对用户进行身份验证,该方法返回 jwt 并验证用户,但应用程序停止使用实际主体提供响应并开始发送空的 200 个响应,有任何线索说明为什么会出现这种情况吗?根据调试器,程序永远不会结束我试图调用的请求,但使用 jwt 的授权总是成功的
我的类(class)
JWTService
@Service
public class JwtService {
private static final String SECRET_KEY = "32bitkey";
public String extractEmail(String token) {
return exctractClaim(token, Claims::getSubject);
}
public <T> T exctractClaim(String token, Function<Claims, T> claimsResolver) {
final Claims claims = extractAllClaims(token);
return claimsResolver.apply(claims);
}
public String generateToken(
UserDetails userDetails) {
return generateToken(Map.of(), userDetails);
}
public String generateToken(
Map<String, Object> extraClaims,
UserDetails userDetails) {
return Jwts
.builder()
.setClaims(extraClaims)
.setSubject(userDetails.getUsername())
.setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 10))
.signWith(getSigningKey(), SignatureAlgorithm.HS256)
.compact();
}
public boolean isTokenValid(String token, UserDetails userDetails) {
final String email = extractEmail(token);
return email.equals(userDetails.getUsername()) && !isTokenExpired(token);
}
public boolean isTokenExpired(String token) {
return extractExpiration(token).before(new Date());
}
private Date extractExpiration(String token) {
return exctractClaim(token, Claims::getExpiration);
}
private Claims extractAllClaims(String token) {
return Jwts
.parserBuilder()
.setSigningKey(getSigningKey())
.build()
.parseClaimsJws(token)
.getBody();
}
private Key getSigningKey() {
byte[] keyBytes = Decoders.BASE64.decode(SECRET_KEY);
return Keys.hmacShaKeyFor(keyBytes);
}
安全过滤器链
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity security) throws Exception {
security
.csrf()
.disable()
.authorizeHttpRequests()
.requestMatchers(
"/fixture/getFixturesBySportAndDate",
"/user/register",
"/user/authenticate",
"/league/getLeaguesByFixturePlayedAtDateInSport",
"/team/fillTeamsHockey",
"/fixture/fillFixturesHockey",
"/fixture/fillFixturesBasketball")
.permitAll()
.anyRequest()
.authenticated()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authenticationProvider(authenticationProvider)
.addFilterBefore(jwtAuthetificationFilter, UsernamePasswordAuthenticationFilter.class);
return security.build();
}
JwtAuthFilter
@Component
@RequiredArgsConstructor
public class JwtAuthetificationFilter extends OncePerRequestFilter {
private final JwtService jwtService;
@Autowired
private UserDetailsService userDetailsService;
@Override
protected void doFilterInternal(
@NonNull HttpServletRequest request,
@NonNull HttpServletResponse response,
@NonNull FilterChain filterChain
) throws ServletException, IOException {
final String authorizationHeader = request.getHeader("Authorization");
final String jwt;
final String email;
if (authorizationHeader == null || !authorizationHeader.startsWith("Bearer ")) {
filterChain.doFilter(request, response);
return;
}
jwt = authorizationHeader.substring(7);
email = jwtService.extractEmail(jwt);
if (email != null && SecurityContextHolder.getContext().getAuthentication() == null){
UserDetails userDetails = userDetailsService.loadUserByUsername(email);
if (jwtService.isTokenValid(jwt, userDetails)){
UsernamePasswordAuthenticationToken authenticationToken
= new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
authenticationToken.setDetails(
new WebAuthenticationDetailsSource().buildDetails(request)
);
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
}
}
}
应用程序配置
@Configuration @RequiredArgsConstructor
public class ApplicationConfig {
private final UserRepository userRepository;
@Bean
public UserDetailsService userDetailsService() {
return username -> userRepository.findByEmail(username)
.orElseThrow(() -> new UsernameNotFoundException("User not found"));
};
@Bean
public AuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
authenticationProvider.setUserDetailsService(userDetailsService());
authenticationProvider.setPasswordEncoder(passwordEncoder());
return authenticationProvider;
}
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration cfg) throws Exception {
return cfg.getAuthenticationManager();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
请求示例
@GetMapping(value = "/getUserInfo")
public ResponseEntity<User> getUserInfo(HttpServletRequest request){
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
String email = authentication.getName();
User user = userRepository.findByEmail(email).get();
return ResponseEntity.ok(user);
}
我尝试基本上更改所有内容并调试信息,但无法完成,因为它只是给了我请求永远不会执行的答案
最佳答案
我根本没有尝试运行您的代码,但乍一看,您在 JwtAuthenticationFilter 中设置安全上下文的身份验证后似乎没有调用过滤器链(尽管如果没有授权 header ,您会这样做)。
尝试调整此:
...
if (email != null && SecurityContextHolder.getContext().getAuthentication() == null){
UserDetails userDetails = userDetailsService.loadUserByUsername(email);
if (jwtService.isTokenValid(jwt, userDetails)){
UsernamePasswordAuthenticationToken authenticationToken
= new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
authenticationToken.setDetails(
new WebAuthenticationDetailsSource().buildDetails(request)
);
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
}
}
调用链中的下一个过滤器:
if (email != null && SecurityContextHolder.getContext().getAuthentication() == null){
UserDetails userDetails = userDetailsService.loadUserByUsername(email);
if (jwtService.isTokenValid(jwt, userDetails)){
UsernamePasswordAuthenticationToken authenticationToken
= new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
authenticationToken.setDetails(
new WebAuthenticationDetailsSource().buildDetails(request)
);
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
}
}
// causes the next filter in the chain to be invoked
filterChain.doFilter(request, response);
以下是 FilterChain header 中对 doFilter
方法的注释:
/**
* Causes the next filter in the chain to be invoked, or if the calling
* filter is the last filter in the chain, causes the resource at the end of
* the chain to be invoked.
*
* @param request
* the request to pass along the chain.
* @param response
* the response to pass along the chain.
*
* @throws IOException if an I/O error occurs during the processing of the
* request
* @throws ServletException if the processing fails for any other reason
*/
public void doFilter(ServletRequest request, ServletResponse response)
throws IOException, ServletException;
关于java - 实现 Spring security Authentication 后,我的应用程序不返回正确的正文,仅返回空的 200 HTTP 响应,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/76492486/