升级到 Spring Boot 3.0.0 时如何修复 WebSecurityConfigurerAdapter 错误?

nhu*_*uvy 9 java spring spring-security spring-boot

我的代码可以在 Spring 2.x 上正常工作。Spring 2.x的源代码

\n

在此输入图像描述

\n

文件CustomFilter.java

\n
package com.example.security;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.ServletRequest;\nimport jakarta.servlet.ServletResponse;\nimport org.springframework.web.filter.GenericFilterBean;\n\nimport java.io.IOException;\n\npublic class CustomFilter extends GenericFilterBean {\n\n    @Override\n    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {\n        chain.doFilter(request, response);\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

文件AuthEntryPointJwt.java

\n
package com.example.security.jwt;\n\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.http.MediaType;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.web.AuthenticationEntryPoint;\nimport org.springframework.stereotype.Component;\n\nimport java.io.IOException;\nimport java.util.HashMap;\nimport java.util.Map;\n\n@Component\npublic class AuthEntryPointJwt implements AuthenticationEntryPoint {\n\n    private static final Logger logger = LoggerFactory.getLogger(AuthEntryPointJwt.class);\n\n    @Override\n    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException {\n        logger.error("Unauthorized error: {}", authException.getMessage());\n        response.setContentType(MediaType.APPLICATION_JSON_VALUE);\n        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);\n        final Map<String, Object> body = new HashMap<>();\n        body.put("status", HttpServletResponse.SC_UNAUTHORIZED);\n        body.put("error", "Unauthorized");\n        body.put("message", authException.getMessage());\n        body.put("path", request.getServletPath());\n        final ObjectMapper mapper = new ObjectMapper();\n        mapper.writeValue(response.getOutputStream(), body);\n    }\n\n}\n
Run Code Online (Sandbox Code Playgroud)\n

文件AuthTokenFilter.java

\n
package com.example.security.jwt;\n\nimport com.example.security.services.UserDetailsServiceImpl;\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.web.authentication.WebAuthenticationDetailsSource;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.filter.OncePerRequestFilter;\n\nimport java.io.IOException;\n\npublic class AuthTokenFilter extends OncePerRequestFilter {\n\n    private static final Logger logger = LoggerFactory.getLogger(AuthTokenFilter.class);\n\n    @Autowired\n    private JwtUtils jwtUtils;\n\n    @Autowired\n    private UserDetailsServiceImpl userDetailsService;\n\n    @Override\n    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {\n        try {\n            String jwt = parseJwt(request);\n            if (jwt != null && jwtUtils.validateJwtToken(jwt)) {\n                String username = jwtUtils.getUserNameFromJwtToken(jwt);\n                UserDetails userDetails = userDetailsService.loadUserByUsername(username);\n                UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());\n                authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));\n                SecurityContextHolder.getContext().setAuthentication(authentication);\n            }\n        } catch (Exception e) {\n            logger.error("Cannot set user authentication: {}", e);\n        }\n        filterChain.doFilter(request, response);\n    }\n\n    private String parseJwt(HttpServletRequest request) {\n        String headerAuth = request.getHeader("Authorization");\n        if (StringUtils.hasText(headerAuth) && headerAuth.startsWith("Bearer ")) {\n            return headerAuth.substring(7);\n        }\n        return null;\n    }\n\n}\n
Run Code Online (Sandbox Code Playgroud)\n

文件JwtUtils.java

\n
package com.example.security.jwt;\n\nimport com.example.security.services.UserDetailsImpl;\nimport io.jsonwebtoken.ExpiredJwtException;\nimport io.jsonwebtoken.Jwts;\nimport io.jsonwebtoken.MalformedJwtException;\nimport io.jsonwebtoken.SignatureAlgorithm;\nimport io.jsonwebtoken.SignatureException;\nimport io.jsonwebtoken.UnsupportedJwtException;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.stereotype.Component;\n\nimport java.util.Date;\n\n@Component\npublic class JwtUtils {\n\n    private static final Logger logger = LoggerFactory.getLogger(JwtUtils.class);\n\n    @Value("${app.jwtSecret}")\n    private String jwtSecret;\n\n    @Value("${app.jwtExpirationMs}")\n    private int jwtExpirationMs;\n\n    public String generateJwtToken(Authentication authentication) {\n        UserDetailsImpl userPrincipal = (UserDetailsImpl) authentication.getPrincipal();\n        return Jwts.builder().setSubject((userPrincipal.getUsername())).setIssuedAt(new Date()).setExpiration(new Date((new Date()).getTime() + jwtExpirationMs)).signWith(SignatureAlgorithm.HS512, jwtSecret).compact();\n    }\n\n    public String getUserNameFromJwtToken(String token) {\n        return Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(token).getBody().getSubject();\n    }\n\n    public boolean validateJwtToken(String authToken) {\n        try {\n            Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(authToken);\n            return true;\n        } catch (SignatureException e) {\n            logger.error("Invalid JWT signature: {}", e.getMessage());\n        } catch (MalformedJwtException e) {\n            logger.error("Invalid JWT token: {}", e.getMessage());\n        } catch (ExpiredJwtException e) {\n            logger.error("JWT token is expired: {}", e.getMessage());\n        } catch (UnsupportedJwtException e) {\n            logger.error("JWT token is unsupported: {}", e.getMessage());\n        } catch (IllegalArgumentException e) {\n            logger.error("JWT claims string is empty: {}", e.getMessage());\n        }\n        return false;\n    }\n\n}\n
Run Code Online (Sandbox Code Playgroud)\n

文件UserDetailsImpl.java

\n
package com.example.security.services;\n\nimport com.example.models.User;\nimport com.fasterxml.jackson.annotation.JsonIgnore;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.core.userdetails.UserDetails;\n\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.stream.Collectors;\n\npublic class UserDetailsImpl implements UserDetails {\n\n    private static final long serialVersionUID = 1L;\n    private Long id;\n    private String username;\n    private String email;\n\n    @JsonIgnore\n    private String password;\n\n    private Collection<? extends GrantedAuthority> authorities;\n\n    public UserDetailsImpl(Long id, String username, String email, String password, Collection<? extends GrantedAuthority> authorities) {\n        this.id = id;\n        this.username = username;\n        this.email = email;\n        this.password = password;\n        this.authorities = authorities;\n    }\n\n    public static UserDetailsImpl build(User user) {\n        List<GrantedAuthority> authorities = user.getRoles().stream().map(role -> new SimpleGrantedAuthority(role.getName().name())).collect(Collectors.toList());\n        return new UserDetailsImpl(user.getId(), user.getUsername(), user.getEmail(), user.getPassword(), authorities);\n    }\n\n    @Override\n    public Collection<? extends GrantedAuthority> getAuthorities() {\n        return authorities;\n    }\n\n    public Long getId() {\n        return id;\n    }\n\n    public String getEmail() {\n        return email;\n    }\n\n    @Override\n    public String getPassword() {\n        return password;\n    }\n\n    @Override\n    public String getUsername() {\n        return username;\n    }\n\n    @Override\n    public boolean isAccountNonExpired() {\n        return true;\n    }\n\n    @Override\n    public boolean isAccountNonLocked() {\n        return true;\n    }\n\n    @Override\n    public boolean isCredentialsNonExpired() {\n        return true;\n    }\n\n    @Override\n    public boolean isEnabled() {\n        return true;\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o)\n            return true;\n        if (o == null || getClass() != o.getClass())\n            return false;\n        UserDetailsImpl user = (UserDetailsImpl) o;\n        return Objects.equals(id, user.id);\n    }\n\n}\n
Run Code Online (Sandbox Code Playgroud)\n

文件UserDetailsServiceImpl.java

\n
package com.example.security.services;\n\nimport com.example.models.User;\nimport com.example.repository.UserRepository;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.core.userdetails.UsernameNotFoundException;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\n\n// Original.\n@Service\npublic class UserDetailsServiceImpl implements UserDetailsService {\n\n    @Autowired\n    UserRepository userRepository;\n\n    @Override\n    @Transactional\n    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {\n        User user = userRepository.findByUsername(username).orElseThrow(() -> new UsernameNotFoundException("User Not Found with username: " + username));\n        return UserDetailsImpl.build(user);\n    }\n\n}\n
Run Code Online (Sandbox Code Playgroud)\n

文件WebSecurityConfig.java

\n
package com.example.security;\n\nimport com.example.security.jwt.AuthEntryPointJwt;\nimport com.example.security.jwt.AuthTokenFilter;\nimport com.example.security.services.UserDetailsServiceImpl;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;\nimport org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;\nimport org.springframework.security.config.http.SessionCreationPolicy;\nimport org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;\nimport org.springframework.security.crypto.password.PasswordEncoder;\nimport org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;\n\n@Configuration\n@EnableWebSecurity\n@EnableGlobalMethodSecurity(\n        // securedEnabled = true,\n        // jsr250Enabled = true,\n        prePostEnabled = true)\npublic class WebSecurityConfig extends WebSecurityConfigurerAdapter {\n\n    @Autowired\n    UserDetailsServiceImpl userDetailsService;\n\n    @Autowired\n    private AuthEntryPointJwt unauthorizedHandler;\n\n    @Bean\n    public AuthTokenFilter authenticationJwtTokenFilter() {\n        return new AuthTokenFilter();\n    }\n\n    @Override\n    public void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {\n        authenticationManagerBuilder.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());\n    }\n\n    @Bean\n    @Override\n    public AuthenticationManager authenticationManagerBean() throws Exception {\n        return super.authenticationManagerBean();\n    }\n\n    @Bean\n    public PasswordEncoder passwordEncoder() {\n        return new BCryptPasswordEncoder();\n    }\n\n\n    // N\xe1\xba\xbfu id g\xe1\xbb\xadi l\xc3\xaan != id c\xe1\xbb\xa7a tenant c\xe1\xbb\xa7a user \xc4\x91\xc3\xb3 trong database, th\xc3\xac kh\xc3\xb4ng cho \xc4\x91i ti\xe1\xba\xbfp.\n    @Override\n    protected void configure(HttpSecurity http) throws Exception {\n        http.cors().and().csrf().disable()\n                .exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()\n                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()\n                //.authorizeRequests().antMatchers("/api/auth/**", "/swagger-ui/**").permitAll()\n\n                .authorizeRequests().antMatchers("/api/auth/**", "/swagger-ui/**", "/v3/api-docs/**").permitAll()\n\n                .antMatchers("/app/**").permitAll()\n                .antMatchers("/api/test/**").permitAll()\n                .anyRequest().authenticated();\n        http.addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class);\n        //;\n\n        // .addFilterAfter(new CustomFilter(), BasicAuthenticationFilter.class); // VyDN 2022_07_22 // https://www.baeldung.com/spring-security-custom-filter\n    }\n\n}\n\n// Add filter before, after: https://stackoverflow.com/a/59000469\n\n
Run Code Online (Sandbox Code Playgroud)\n

现在,我使用 Java / JDK 19,Spring Boot 3.0.0 。升级到 Spring Boot 3.0.0 后,导致语法错误。在此输入图像描述

\n

在此输入图像描述

\n

如何修复WebSecurityConfigurerAdapter升级到 Spring Boot 3.0.0 时出现的错误?具体到我的配置。请指导我重写文件WebSecurityConfig.java

\n

sof*_*ser 9

WebSecurityConfigurerAdapter 已弃用,应使用基于组件的安全配置。您必须为 HTTPSecurity 创建一个 SecurityFilterChain bean,并且不应按照其他答案的建议扩展WebSecurityConfigurerAdapter。请参阅https://spring.io/blog/2022/02/21/spring-security-without-the-websecurityconfigureradapter了解更多详细信息。


小智 1

在 Spring Boot 3 上WebSecurityConfigurerAdapter已弃用。因此,在您的情况下,该类WebSecurityConfig不应扩展任何类,并且大多数类应由其自身实现。您可以自己将 实现userDetailsService为 a@Bean并设置AuthenticationManager,而不仅仅是返回 super 。我遇到了同样的问题,我的解决方案只是在类中的注释@SuppressWarnings("deprecation") 之前添加。@Configuration