我从一篇文章here中了解到这一点
我正在使用Sping Boot 3和Java 21来编写代码。
根据文章,我只自定义了类名从JwtService到CredentialManager
下面的版本
@Component
@RequiredArgsConstructor
public class JwtAuthenticationFilter extends OncePerRequestFilter {
private final CredentialManager credentialManager;
private final UserService userService;
@Override
protected void doFilterInternal(@NonNull HttpServletRequest request,
@NonNull HttpServletResponse response, @NonNull FilterChain filterChain)
throws ServletException, IOException {
final String authHeader = request.getHeader("Authorization");
final String jwt;
final String username;
if (StringUtils.isEmpty(authHeader) || !StringUtils.startsWith(authHeader, "Bearer ")) {
filterChain.doFilter(request, response);
return;
}
jwt = authHeader.substring(7);
username = credentialManager.extractUsername(jwt);
if (StringUtils.isNotEmpty(username) && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = userService.userDetailsService().loadUserByUsername(username);
if (credentialManager.isTokenValid(jwt, userDetails)) {
SecurityContext context = SecurityContextHolder.createEmptyContext();
UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
context.setAuthentication(authToken);
SecurityContextHolder.setContext(context);
}
}
filterChain.doFilter(request, response);
}
}
字符串
自定义配置文件
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfiguration {
private final JwtAuthenticationFilter jwtAuthenticationFilter;
private final UserService userService;
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.csrf(AbstractHttpConfigurer::disable)
.authorizeHttpRequests(request -> request.requestMatchers("/user/**")
.permitAll().anyRequest().authenticated())
.sessionManagement(manager -> manager.sessionCreationPolicy(STATELESS))
.authenticationProvider(authenticationProvider())
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
return http.build();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public AuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setUserDetailsService(userService.userDetailsService());
authProvider.setPasswordEncoder(passwordEncoder());
return authProvider;
}
@Bean
public AuthenticationManager authenticationManager(@Lazy AuthenticationConfiguration config) throws Exception {
return config.getAuthenticationManager();
}
}
型
服务
@Service
public class CredentialManager {
@Value("${token.secret-key}")
private String jwtSigningKey;
public String extractUsername(String token) {
return extractClaim(token, Claims::getSubject);
}
public String generateToken(UserDetails userDetails) {
return generateToken(new HashMap<>(), userDetails);
}
public boolean isTokenValid(String token, UserDetails userDetails) {
final String userName = extractUsername(token);
return (userName.equals(userDetails.getUsername())) && !isTokenExpired(token);
}
private <T> T extractClaim(String token, Function<Claims, T> claimsResolvers) {
final Claims claims = extractAllClaims(token);
return claimsResolvers.apply(claims);
}
private 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 * 24))
.signWith(getSigningKey(), SignatureAlgorithm.HS256).compact();
}
private boolean isTokenExpired(String token) {
return extractExpiration(token).before(new Date());
}
private Date extractExpiration(String token) {
return extractClaim(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(jwtSigningKey);
return Keys.hmacShaKeyFor(keyBytes);
}
}
x
@Service
@RequiredArgsConstructor
public class UserServiceImpl implements UserService {
private final UserRepository userRepository;
private final LoginServiceImpl loginService;
private final SignUpServiceImpl signUpService;
@Override
public UserDetailsService userDetailsService() {
return username -> (UserDetails) userRepository.findByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException("User not found"));
}
@Override
public LoginResponse login(LoginRequest request) {
return loginService.login(request);
}
@Override
public ResponseBody signUp(SignUpRequest request) {
return signUpService.signUp(request);
}
}
@Service
@RequiredArgsConstructor
public class LoginServiceImpl {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
private final CredentialManager credentialManager;
private final UserRepository userRepository;
private final AuthenticationManager authenticationManager;
public LoginResponse login(LoginRequest request) {
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(request.username(), request.password()));
var user = userRepository.findByUsername(request.username())
.orElseThrow(() -> {
logger.error("Invalid username. Username: {}", request.username());
return new BusinessException("Invalid username.");
});
var jwt = credentialManager.generateToken((UserDetails) user);
return new LoginResponse(jwt);
}
}
的一种或多种
我在运行应用程序时收到错误消息
┌─────┐
| jwtAuthenticationFilter defined in file [/configuration/JwtAuthenticationFilter.class]
↑ ↓
| userServiceImpl defined in file [/service/impl/UserServiceImpl.class]
↑ ↓
| loginServiceImpl defined in file [/service/impl/user/LoginServiceImpl.class]
↑ ↓
| securityConfiguration defined in file [/configuration/SecurityConfiguration.class]
└─────┘
型
行动:不鼓励依赖循环引用,默认情况下禁止使用循环引用。更新您的应用程序以删除bean之间的依赖循环。作为最后的手段,可以通过将spring.main.allow-circular-references设置为true来自动打破循环。
有人能帮忙吗?是因为在服务类中被多次启动吗?
真的很感激
1条答案
按热度按时间d7v8vwbk1#
我的提议是使用转换器<Jwt,JwtAuthenticationToken>来填充UserDetails,并使用默认流来验证令牌OAuth2TokenValidator