新版SpringSecurity配置(SpringBoot>2.7&SpringSecurity>5.7)

新版SpringSecurityConfig

在使用SpringBoot2.7或者SpringSecurity5.7以上版本时,会提示:

在 Spring Security 5.7.0-M2 中,我们弃用了WebSecurityConfigurerAdapter,因为我们鼓励用户转向基于组件的安全配置。

所以之前那种通过继承WebSecurityConfigurerAdapter的方式的配置组件是不行的。

同时也会遇到很多问题,例如:

在向SpringSecurity过滤器链中添加过滤器时(例如:JWT支持,第三方验证),我们需要注入AuthenticationManager对象等问题。

故在此记录一下SpringSecurity的一些基础配置项:

1 网络安全配置,忽略部分路径(如静态文件路径)

@Bean
public WebSecurityCustomizer webSecurityCustomizer() {
     return (web) -> web.ignoring().antMatchers("/ignore1", "/ignore2");
}

2 设置中文配置

@Bean
public ReloadableResourceBundleMessageSource messageSource() {
    ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
    // 设置中文配置
    messageSource.setBasename("classpath:org/springframework/security/messages_zh_CN");
    return messageSource;
}

3 设置密码编码器

@Bean
@ConditionalOnMissingBean
public PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
}

4 取消ROLE_ prefix

@Bean
@ConditionalOnMissingBean
public GrantedAuthorityDefaults grantedAuthorityDefaults() {
    // Remove the ROLE_ prefix
    return new GrantedAuthorityDefaults("");
}

5 暴露本地认证管理器(AuthenticationManager)

/**
 * 认证管理器,登录的时候参数会传给 authenticationManager
 */
@Bean(name = BeanIds.AUTHENTICATION_MANAGER)
public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
    return authenticationConfiguration.getAuthenticationManager();
}

6 其他配置

import com.example.websocket.chat.security.filer.CustomUsernamePasswordAuthenticationFilter;
import com.example.websocket.chat.security.filer.JwtAuthenticationFilter;
import com.example.websocket.chat.security.handler.*;
import com.example.websocket.chat.security.service.JwtStoreService;
import com.example.websocket.chat.security.service.impl.UserDetailsServiceImpl;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.support.ReloadableResourceBundleMessageSource;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.BeanIds;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
import org.springframework.security.config.core.GrantedAuthorityDefaults;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutFilter;

import javax.annotation.Resource;

/**
 * @author zhong
 */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true)
public class SpringSecurityConfig {

    @Resource
    private CustomAuthenticationSuccessHandler customAuthenticationSuccessHandler;

    @Resource
    private CustomAuthenticationFailureHandler customAuthenticationFailureHandler;

    @Resource
    private CustomAuthenticationEntryPoint customAuthenticationEntryPoint;

    @Resource
    private CustomLogoutHandler customLogoutHandler;

    @Resource
    private CustomLogoutSuccessHandler customLogoutSuccessHandler;

    @Resource
    private CustomAccessDeniedHandler customAccessDeniedHandler;


    @Resource
    private SecurityProperties securityProperties;

    @Resource
    private JwtStoreService jwtStoreService;

    @Resource
    private UserDetailsServiceImpl userDetailsService;

    @Resource
    private AuthenticationConfiguration authenticationConfiguration;

    /**
     * 静态文件放行
     */
    @Bean
    public WebSecurityCustomizer webSecurityCustomizer() {
        return (web) -> web.ignoring().antMatchers(securityProperties.getStaticPaths());
    }

    /**
     * 取消ROLE_前缀
     */
    @Bean
    public GrantedAuthorityDefaults grantedAuthorityDefaults() {
        // Remove the ROLE_ prefix
        return new GrantedAuthorityDefaults("");
    }

    /**
     * 设置密码编码器
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    /**
     * 设置中文配置
     */
    @Bean
    public ReloadableResourceBundleMessageSource messageSource() {
        ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
        messageSource.setBasename("classpath:org/springframework/security/messages_zh_CN");
        return messageSource;
    }

    /**
     * 认证管理器,登录的时候参数会传给 authenticationManager
     */
    @Bean
    public AuthenticationManager authenticationManager() throws Exception {
        return authenticationConfiguration.getAuthenticationManager();
    }

    /**
     * 设置默认认证提供
     */
    @Bean
    public DaoAuthenticationProvider daoAuthenticationProvider() {
        final DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
        authenticationProvider.setUserDetailsService(userDetailsService);
        authenticationProvider.setPasswordEncoder(passwordEncoder());
        return authenticationProvider;
    }

    /**
     * 安全配置
     */
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http, AuthenticationConfiguration authenticationConfiguration) throws Exception {

        // 表单
        http.formLogin()
                // 登录成功处理器
                .successHandler(customAuthenticationSuccessHandler)
                // 登录错误处理器
                .failureHandler(customAuthenticationFailureHandler)
                .and()
                //添加登录逻辑拦截器,不使用默认的UsernamePasswordAuthenticationFilter
                .addFilterBefore(
                        new CustomUsernamePasswordAuthenticationFilter(
                                authenticationManager(),
                                customAuthenticationSuccessHandler,
                                customAuthenticationFailureHandler
                        )
                        , UsernamePasswordAuthenticationFilter.class)
                //添加token验证过滤器
                .addFilterBefore(new JwtAuthenticationFilter(jwtStoreService), LogoutFilter.class);
        //退出
        http
                .logout()
                // URL
                .logoutUrl("/user/logout")
                // 登出处理
                .addLogoutHandler(customLogoutHandler)
                // 登出成功处理
                .logoutSuccessHandler(customLogoutSuccessHandler);
        //拦截设置
        http
                .authorizeRequests()
                //公开以下urls
                .antMatchers(securityProperties.getPublicPaths()).permitAll()
                //其他路径必须验证
                .anyRequest().authenticated();


        //异常处理
        http
                .exceptionHandling()
                // 未登录处理
                .authenticationEntryPoint(customAuthenticationEntryPoint)
                // 无权限处理
                .accessDeniedHandler(customAccessDeniedHandler);
        //关闭session
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        // 关闭cors
        http.cors().disable();
        // 关闭csrf
        http.csrf().disable();
        // 关闭headers
        http.headers().frameOptions().disable();
        return http.build();
    }
}

  • 8
    点赞
  • 44
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论
Spring Boot 2.7 还没有发布,可能是您想说的是 Spring Boot 2.4。 在 Spring Boot 2.4 中,配置 Spring Security 和 JWT 的步骤如下: 1. 添加依赖 在 pom.xml 文件中添加 Spring Security 和 JWT 的依赖: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-api</artifactId> <version>0.11.2</version> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-impl</artifactId> <version>0.11.2</version> <scope>runtime</scope> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-jackson</artifactId> <version>0.11.2</version> <scope>runtime</scope> </dependency> ``` 2. 配置 Spring SecuritySpring Security配置类中,添加以下代码: ```java @Configuration @EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private JwtTokenFilter jwtTokenFilter; @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable() .authorizeRequests() .antMatchers("/api/authenticate").permitAll() .anyRequest().authenticated() .and() .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .addFilterBefore(jwtTokenFilter, UsernamePasswordAuthenticationFilter.class); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.inMemoryAuthentication() .withUser("user").password(passwordEncoder().encode("password")).roles("USER"); } } ``` 这个配置类中,我们首先禁用了 CSRF 防护,然后配置了访问控制,允许 `/api/authenticate` 路径的请求不需要认证,其他路径的请求需要进行认证。同时,我们还配置了使用 JWT 进行认证,通过 `JwtTokenFilter` 过滤器来实现。 3. 配置 JWT 在 JWT 的配置类中,添加以下代码: ```java @Configuration public class JwtConfig { @Value("${jwt.secret}") private String secret; @Bean public JwtParser jwtParser() { return Jwts.parser().setSigningKey(secret); } @Bean public JwtEncoder jwtEncoder() { return Jwts.builder().setSigningKey(secret).build(); } @Bean public JwtTokenFilter jwtTokenFilter() { return new JwtTokenFilter(jwtParser()); } } ``` 在这个配置类中,我们首先从配置文件中读取了 JWT 的密钥,然后配置了 `JwtParser` 和 `JwtEncoder` 对象,用于解析和生成 JWT。最后,我们还配置了 `JwtTokenFilter` 过滤器。这个过滤器用于在每个请求中解析 JWT,并将解析后的信息传递给 Spring Security 进行认证。 4. 编写 JwtTokenFilter 最后,我们需要编写 `JwtTokenFilter` 过滤器。这个过滤器用于在每个请求中解析 JWT,并将解析后的信息传递给 Spring Security 进行认证。具体代码如下: ```java public class JwtTokenFilter extends OncePerRequestFilter { private final JwtParser jwtParser; public JwtTokenFilter(JwtParser jwtParser) { this.jwtParser = jwtParser; } @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException { String header = request.getHeader("Authorization"); if (header != null && header.startsWith("Bearer ")) { String token = header.substring(7); try { Jws<Claims> claimsJws = jwtParser.parseClaimsJws(token); String username = claimsJws.getBody().getSubject(); List<SimpleGrantedAuthority> authorities = new ArrayList<>(); authorities.add(new SimpleGrantedAuthority("ROLE_USER")); UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, null, authorities); SecurityContextHolder.getContext().setAuthentication(authenticationToken); } catch (Exception e) { SecurityContextHolder.clearContext(); } } chain.doFilter(request, response); } } ``` 在这个过滤器中,我们首先从请求头中获取 JWT,然后使用 `JwtParser` 对象对 JWT 进行解析。如果解析成功,我们就从 JWT 中获取用户名,并创建一个 `UsernamePasswordAuthenticationToken` 对象,用于在 Spring Security 中进行认证。最后,我们将认证信息保存到 `SecurityContextHolder` 中,以便于后续的处理。如果解析失败,我们就清空 `SecurityContextHolder` 中的认证信息。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

卑微小钟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值