欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Spring Security6配置方法(廢棄WebSecurityConfigurerAdapter)

 更新時間:2023年12月29日 10:20:35   作者:markvivv  
本文主要介紹了Spring Security6配置方法(廢棄WebSecurityConfigurerAdapter),就像文章標題所說的,SpringSecurity已經廢棄了繼承WebSecurityConfigurerAdapter的配置方式,下面就來詳細的介紹一下,感興趣的可以了解一下

一、背景

最近阿里云的項目遷回本地運行,數據庫從阿里云的RDS(即Mysql5.6)換成了本地8.0,Redis也從古董級別的2.x換成了現(xiàn)在6,忍不住,手癢,把jdk升級到了17,用zgc垃圾回收器,源代碼重新編譯重新發(fā)布,結果碰到了古董的SpringBoot不支持jdk17,所以有了這篇日志。記錄一下SpringBoot2+SpringSecurity+JWT升級成SpringBoot3+SpringSecurity+JWT,就像文章標題所說的,SpringSecurity已經廢棄了繼承WebSecurityConfigurerAdapter的配置方式,那就的從頭來咯。

在Spring Security 5.7.0-M2中,Spring就廢棄了WebSecurityConfigurerAdapter,因為Spring官方鼓勵用戶轉向基于組件的安全配置。本文整理了一下新的配置方法。 

在下面的例子中,我們使用Spring Security lambda DSL和HttpSecurity#authorizeHttpRequests方法來定義我們的授權規(guī)則,從而遵循最佳實踐。

二、配置成功后根據配置的情況整理的類圖

三、配置詳情

3.1. 啟用WebSecurity配置 

@Configuration
@EnableWebSecurity
public class SecurityConfig {

}

 以下的配置在SecurityConfig內完成。

3.1.1. 注冊SecurityFilterChain Bean,并完成HttpSecurity配置

在HttpSecurity中注意使用了lambda寫法,使用這種寫法之后,每個設置都直接返回HttpSecurity對象,避免了多余的and()操作符。每一步具體的含義,請參考代碼上的注釋。

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
                // 禁用basic明文驗證
                .httpBasic().disable()
                // 前后端分離架構不需要csrf保護
                .csrf().disable()
                // 禁用默認登錄頁
                .formLogin().disable()
                // 禁用默認登出頁
                .logout().disable()
                // 設置異常的EntryPoint,如果不設置,默認使用Http403ForbiddenEntryPoint
                .exceptionHandling(exceptions -> exceptions.authenticationEntryPoint(invalidAuthenticationEntryPoint))
                // 前后端分離是無狀態(tài)的,不需要session了,直接禁用。
                .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                .authorizeHttpRequests(authorizeHttpRequests -> authorizeHttpRequests
                        // 允許所有OPTIONS請求
                        .requestMatchers(HttpMethod.OPTIONS, "/**").permitAll()
                        // 允許直接訪問授權登錄接口
                        .requestMatchers(HttpMethod.POST, "/web/authenticate").permitAll()
                        // 允許 SpringMVC 的默認錯誤地址匿名訪問
                        .requestMatchers("/error").permitAll()
                        // 其他所有接口必須有Authority信息,Authority在登錄成功后的UserDetailsImpl對象中默認設置“ROLE_USER”
                        //.requestMatchers("/**").hasAnyAuthority("ROLE_USER")
                        // 允許任意請求被已登錄用戶訪問,不檢查Authority
                        .anyRequest().authenticated())
                .authenticationProvider(authenticationProvider())
                // 加我們自定義的過濾器,替代UsernamePasswordAuthenticationFilter
                .addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class);

        return http.build();
    }

3.1.2. 注冊自定義用戶登錄信息查詢Bean

    @Autowired
    private UserDetailsService userDetailsService;

    @Bean
    public UserDetailsService userDetailsService() {
        // 調用 JwtUserDetailService實例執(zhí)行實際校驗
        return username -> userDetailsService.loadUserByUsername(username);
    }

 這里關聯(lián)到一個自定義的子類UserDetailsService,代碼邏輯如下,注意需要根據實際情況改造數據庫查詢邏輯:

@Component
public class SecurityUserDetailsService implements UserDetailsService {

    @Autowired
    private SqlSession sqlSession;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        try {
            // 查詢數據庫用戶表,獲得用戶信息
            sqlSession.xxx
            // 使用獲得的信息創(chuàng)建SecurityUserDetails
            SecurityUserDetails user = new SecurityUserDetails(username, 
                    password,
                    // 以及其他org.springframework.security.core.userdetails.UserDetails接口要求的信息
                    );

            logger.info("用戶信息:{}", user);
            return user;
        } catch (Exception e) {
            String msg = "Username: " + username + " not found";
            logger.error(msg, e);
            throw new UsernameNotFoundException(msg);
        }
    }
}

3.1.3. 注冊密碼加密Bean

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

3.1.4. 注冊用戶授權檢查執(zhí)行Bean 

這里設定使用DaoAuthenticationProvider執(zhí)行具體的校驗檢查,并且將自定義的用戶登錄查詢服務Bean,和密碼生成器都注入到該對象中

    /**
     * 調用loadUserByUsername獲得UserDetail信息,在AbstractUserDetailsAuthenticationProvider里執(zhí)行用戶狀態(tài)檢查
     *
     * @return
     */
    @Bean
    public AuthenticationProvider authenticationProvider() {
        DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
        // DaoAuthenticationProvider 從自定義的 userDetailsService.loadUserByUsername 方法獲取UserDetails
        authProvider.setUserDetailsService(userDetailsService());
        // 設置密碼編輯器
        authProvider.setPasswordEncoder(passwordEncoder());
        return authProvider;
    }

3.1.5. 注冊授權檢查管理Bean 

    /**
     * 登錄時需要調用AuthenticationManager.authenticate執(zhí)行一次校驗
     *
     * @param config
     * @return
     * @throws Exception
     */
    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {
        return config.getAuthenticationManager();
    }

3.1.6. 注冊每次請求的jwt檢查攔截器

在攔截器中檢查jwt是否能夠通過簽名驗證,是否還在有效期內。如果通過驗證,使用jwt中的信息生成一個不含密碼信息的SecurityUserDetails對象,并設置到SecurityContext中,確保后續(xù)的過濾器檢查能夠知曉本次請求是被授權過的。具體代碼邏輯看3.2. jwt請求過濾器。

    @Bean
    public JwtTokenOncePerRequestFilter authenticationJwtTokenFilter() {
        return new JwtTokenOncePerRequestFilter();
    }

3.1.7.  SecurityConfig 對象完整內容

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private InvalidAuthenticationEntryPoint invalidAuthenticationEntryPoint;

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

    @Bean
    public JwtTokenOncePerRequestFilter authenticationJwtTokenFilter() {
        return new JwtTokenOncePerRequestFilter();
    }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
                // 禁用basic明文驗證
                .httpBasic().disable()
                // 前后端分離架構不需要csrf保護
                .csrf().disable()
                // 禁用默認登錄頁
                .formLogin().disable()
                // 禁用默認登出頁
                .logout().disable()
                // 設置異常的EntryPoint,如果不設置,默認使用Http403ForbiddenEntryPoint
                .exceptionHandling(exceptions -> exceptions.authenticationEntryPoint(invalidAuthenticationEntryPoint))
                // 前后端分離是無狀態(tài)的,不需要session了,直接禁用。
                .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                .authorizeHttpRequests(authorizeHttpRequests -> authorizeHttpRequests
                        // 允許所有OPTIONS請求
                        .requestMatchers(HttpMethod.OPTIONS, "/**").permitAll()
                        // 允許直接訪問授權登錄接口
                        .requestMatchers(HttpMethod.POST, "/web/authenticate").permitAll()
                        // 允許 SpringMVC 的默認錯誤地址匿名訪問
                        .requestMatchers("/error").permitAll()
                        // 其他所有接口必須有Authority信息,Authority在登錄成功后的UserDetailsImpl對象中默認設置“ROLE_USER”
                        //.requestMatchers("/**").hasAnyAuthority("ROLE_USER")
                        // 允許任意請求被已登錄用戶訪問,不檢查Authority
                        .anyRequest().authenticated())
                .authenticationProvider(authenticationProvider())
                // 加我們自定義的過濾器,替代UsernamePasswordAuthenticationFilter
                .addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class);

        return http.build();
    }

    @Bean
    public UserDetailsService userDetailsService() {
        // 調用 JwtUserDetailService實例執(zhí)行實際校驗
        return username -> userDetailsService.loadUserByUsername(username);
    }

    /**
     * 調用loadUserByUsername獲得UserDetail信息,在AbstractUserDetailsAuthenticationProvider里執(zhí)行用戶狀態(tài)檢查
     *
     * @return
     */
    @Bean
    public AuthenticationProvider authenticationProvider() {
        DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
        // DaoAuthenticationProvider 從自定義的 userDetailsService.loadUserByUsername 方法獲取UserDetails
        authProvider.setUserDetailsService(userDetailsService());
        // 設置密碼編輯器
        authProvider.setPasswordEncoder(passwordEncoder());
        return authProvider;
    }

    /**
     * 登錄時需要調用AuthenticationManager.authenticate執(zhí)行一次校驗
     *
     * @param config
     * @return
     * @throws Exception
     */
    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {
        return config.getAuthenticationManager();
    }
}

3.2. JWT請求過濾檢查

/**
 * 每次請求的 Security 過濾類。執(zhí)行jwt有效性檢查,如果失敗,不會設置 SecurityContextHolder 信息,會進入 AuthenticationEntryPoint
 */
public class JwtTokenOncePerRequestFilter extends OncePerRequestFilter {

    @Autowired
    private JwtTokenProvider jwtTokenProvider;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        try {
            String token = jwtTokenProvider.resolveToken(request);
            if (token != null && jwtTokenProvider.validateToken(token)) {
                Authentication auth = jwtTokenProvider.getAuthentication(token);
                
                if (auth != null) {
                    SecurityContextHolder.getContext().setAuthentication(auth);
                }
            }
        } catch (Exception e) {
            logger.error("Cannot set user authentication!", e);
        }

        filterChain.doFilter(request, response);
    }

}

3.3. 登錄校驗

 考慮到jwt簽名驗簽的可靠性,以及jwt的有效載荷并未被加密,所以jwt中放置了UserDetails接口除密碼字段外其他所有字段以及項目所需的業(yè)務字段。兩個目的,1. 瀏覽器端可以解碼jwt的有效載荷部分的內容用于業(yè)務處理,由于不涉及到敏感信息,不擔心泄密;2.服務端可以通過jwt的信息重新生成UserDetails對象,并設置到SecurityContext中,用于請求攔截的授權校驗。

代碼如下:

@RestController
@RequestMapping("/web")
public class AuthController {

    @PostMapping(value="/authenticate")
    public ResponseEntity<?> authenticate(@RequestBody Map<String, String> param) {
        logger.debug("登錄請求參數:{}", param);

        try {
            String username = param.get("username");
            String password = param.get("password");
            String reqType = param.get("type");

            // 傳遞用戶密碼給到SpringSecurity執(zhí)行校驗,如果校驗失敗,會進入BadCredentialsException
            Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
            // 驗證通過,設置授權信息至SecurityContextHolder
            SecurityContextHolder.getContext().setAuthentication(authentication);

            // 如果驗證通過了,從返回的authentication里獲得完整的UserDetails信息
            SecurityUserDetails userDetails = (SecurityUserDetails) authentication.getPrincipal();

            // 將用戶的ID、名稱等信息保存在jwt的token中
            String token = jwtTokenProvider.createToken(userDetails.getUsername(), userDetails.getCustname(), new ArrayList<>());

            // 設置cookie,本項目中非必需,因以要求必須傳head的Authorization: Bearer 參數
            ResponseCookie jwtCookie = jwtTokenProvider.generateJwtCookie(token);
            Map<String, Object> model = new HashMap<>();
            model.put("username", username);
            model.put("token", token);
            return ok().body(RespBody.build().ok("登錄成功", model));
        } catch (BadCredentialsException e) {
            return ok(RespBody.build().fail("賬號或密碼錯誤!"));
        }
    }
}

4. 總結 

經過以上步驟,對SpringSecurity6結合jwt機制進行校驗的過程就全部完成了,jwt的工具類可以按照項目自己的情況進行編碼。這里GitHub - markvivv/springboot-security-jwt-example: Spring Boot + Spring Security + JSON Web Token(JWT) + Mybatis完整示例,包含SpringBoot2和Spring Boot3兩種方式。)有完整的SpringBoot3+SpringSecurity6+jwt的示例工程。目前有一個SpringBoot2的老版本在這里。jwt的工具類也可以參考老版本的這個。

到此這篇關于Spring Security6配置方法(廢棄WebSecurityConfigurerAdapter)的文章就介紹到這了,更多相關Spring Security6配置內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • Java控制流程示例代碼詳解

    Java控制流程示例代碼詳解

    這篇文章主要介紹了Java控制流程,本文通過示例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-03-03
  • Java面向對象程序設計多態(tài)性示例

    Java面向對象程序設計多態(tài)性示例

    這篇文章主要介紹了Java面向對象程序設計多態(tài)性,結合實例形式分析了java多態(tài)性的概念、原理、定義與使用方法及相關注意事項,需要的朋友可以參考下
    2018-03-03
  • Java生成隨機數的2種示例方法代碼

    Java生成隨機數的2種示例方法代碼

    在Java中,生成隨機數有兩種方法。1是使用Random類。2是使用Math類中的random方法??聪旅娴睦邮褂冒?/div> 2013-11-11
  • 談談Java中整數類型(short int long)的存儲方式

    談談Java中整數類型(short int long)的存儲方式

    在java中的整數類型有四種,分別是byte short in long,本文重點給大家介紹java中的整數類型(short int long),由于byte只是一個字節(jié)0或1,在此就不多說了,對java中的整數類型感興趣的朋友一起學習吧
    2015-11-11
  • MybatisPlus中QueryWrapper常用方法總結

    MybatisPlus中QueryWrapper常用方法總結

    MyBatis-Plus是一個Mybatis增強版工具,在MyBatis上擴充了其他功能沒有改變其基本功能,為了簡化開發(fā)提交效率而存在,queryWrapper是mybatis plus中實現(xiàn)查詢的對象封裝操作類,本文就給大家總結了MybatisPlus中QueryWrapper的常用方法,需要的朋友可以參考下
    2023-07-07
  • idea2023設置啟動參數、單元測試啟動參數

    idea2023設置啟動參數、單元測試啟動參數

    在使用IDEA進行開發(fā)時,我們可以通過設置一些啟動參數來優(yōu)化開發(fā)環(huán)境的性能和體驗,具有一定的參考價值,感興趣的可以了解一下
    2023-11-11
  • java精度計算代碼 java指定精確小數位

    java精度計算代碼 java指定精確小數位

    這篇文章主要為大家詳細介紹了java精度計算代碼,java指定精確小數位,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-02-02
  • RocketMQ實現(xiàn)消息分發(fā)的步驟

    RocketMQ實現(xiàn)消息分發(fā)的步驟

    RocketMQ 實現(xiàn)消息分發(fā)的核心機制是通過 Topic、Queue 和 Consumer Group 的配合實現(xiàn)的,下面給大家介紹RocketMQ實現(xiàn)消息分發(fā)的步驟,感興趣的朋友一起看看吧
    2024-03-03
  • SpringBoot配置Redis實現(xiàn)保存獲取和刪除數據

    SpringBoot配置Redis實現(xiàn)保存獲取和刪除數據

    本文主要介紹了SpringBoot配置Redis實現(xiàn)保存獲取和刪除數據,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,感興趣的小伙伴們可以參考一下
    2021-06-06
  • Java 創(chuàng)建PDF打印小冊子案例

    Java 創(chuàng)建PDF打印小冊子案例

    這篇文章主要給大家分享Java 創(chuàng)建PDF打印小冊子案例,PDF打印小冊子是指將PDF格式文檔在打印成刊物前需要提前進行的頁面排版,以便在打印后裝訂成冊,下面文章內容我們將下面以Java代碼展示如何來實現(xiàn),需要的朋友可以參考一下
    2021-10-10

最新評論