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

SpringBoot3集成SpringSecurity+JWT的實(shí)現(xiàn)

 更新時(shí)間:2025年07月23日 08:35:17   作者:Harry技術(shù)  
本文詳解SpringBoot3整合SpringSecurity與JWT實(shí)現(xiàn)認(rèn)證授權(quán),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧

準(zhǔn)備工作

概述: 在本文中,我們將一步步學(xué)習(xí)如何使用 Spring Boot 3 和 Spring Security 來(lái)保護(hù)我們的應(yīng)用程序。我們將從簡(jiǎn)單的入門開始,然后逐漸引入數(shù)據(jù)庫(kù),并最終使用 JWT 實(shí)現(xiàn)前后端分離。

引入依賴

這里主要用到了Mybatis-plus、hutool 、knife4j ,其他依賴可以直接勾選

<properties>
        <java.version>17</java.version>

        <mybatisplus.version>3.5.9</mybatisplus.version>
        <knife4j.version>4.5.0</knife4j.version>
        <hutool.version>5.8.26</hutool.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!-- MyBatis-Plus https://baomidou.com-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-jsqlparser</artifactId>
        </dependency>

        <!--Knife4j https://doc.xiaominfo.com/-->
        <dependency>
            <groupId>com.github.xiaoymin</groupId>
            <artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
            <version>${knife4j.version}</version>
        </dependency>

        <!-- Java工具類庫(kù) https://doc.hutool.cn -->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>${hutool.version}</version>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-bom</artifactId>
                <version>${mybatisplus.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

我這里使用的Spring boot版本為3.3.5 ,使用3.4.0整合JWT過濾器時(shí),打開swagger會(huì)報(bào)錯(cuò):jakarta.servlet.ServletException: Handler dispatch failed: java.lang.NoSuchMethodError: 'void org.springframework.web.method.ControllerAdviceBean.<init>(java.lang.Object) ,說(shuō)是版本兼容問題。暫時(shí)沒有找到很好的解決方案,所以給Spring boot版本降至3.3.5。

設(shè)計(jì)表結(jié)構(gòu)

關(guān)于表結(jié)構(gòu)內(nèi)容我這里不詳細(xì)的說(shuō)了,各個(gè)表字段內(nèi)容,可以拉一下代碼,獲取表結(jié)構(gòu)sql腳本。關(guān)注公眾號(hào):“Harry技術(shù)”,回復(fù)“jwt”,即可獲取到整個(gè)項(xiàng)目源碼以及表結(jié)構(gòu)。

sys_config 系統(tǒng)配置表
sys_dept 部門表
sys_dict 字典表
sys_dict_data 字典數(shù)據(jù)表
sys_menu 菜單表
sys_role 角色表
sys_role_menu 角色菜單關(guān)系表
sys_user 用戶表
sys_user_role 用戶角色關(guān)系表

生成基本代碼

白名單配置

因?yàn)槲覀冞@里引入knife4j ,關(guān)于knife4j 的相關(guān)配置可以參考《Spring Boot 3 整合Knife4j(OpenAPI3規(guī)范)》,我們需要將以下接口加入到白名單

# 白名單列表
  ignore-urls:
    - /v3/api-docs/**
    - /doc.html
    - /swagger-resources/**
    - /webjars/**
    - /swagger-ui/**
    - /swagger-ui.html

JWT配置

JWT(JSON Web Token)相關(guān)資料網(wǎng)絡(luò)上非常多,可以自行搜索,簡(jiǎn)單點(diǎn)說(shuō)JWT就是一種網(wǎng)絡(luò)身份認(rèn)證和信息交換格式。

  • Header 頭部信息,主要聲明了JWT的簽名算法等信息
  • Payload 載荷信息,主要承載了各種聲明并傳遞明文數(shù)據(jù)
  • Signature 簽名,擁有該部分的JWT被稱為JWS,也就是簽了名的JWT,用于校驗(yàn)數(shù)據(jù)

整體結(jié)構(gòu)是:

header.payload.signature

配置參數(shù)jwt密碼、過期時(shí)間等

yml 配置

# 安全配置
security:
  jwt:
    # JWT 秘鑰
    key: www.tech-harry.cn
    # JWT 有效期(單位:秒)
    ttl: 7200
  # 白名單列表
  ignore-urls:
    - /v3/api-docs/**
    - /doc.html
    - /swagger-resources/**
    - /webjars/**
    - /swagger-ui/**
    - /swagger-ui.html
    - /auth/login

創(chuàng)建SecurityProperties

/**
 * Security Properties
 *
 * @author harry
 * @公眾號(hào) Harry技術(shù)
 */
@Data
@ConfigurationProperties(prefix = "security")
public class SecurityProperties {

    /**
     * 白名單 URL 集合
     */
    private List<String> ignoreUrls;

    /**
     * JWT 配置
     */
    private JwtProperty jwt;


    /**
     * JWT 配置
     */
    @Data
    public static class JwtProperty {

        /**
         * JWT 密鑰
         */
        private String key;

        /**
         * JWT 過期時(shí)間
         */
        private Long ttl;

    }
}

自定義未授權(quán)和未登錄結(jié)果返回

在之前的案例中沒有自定義未授權(quán)和未登錄,直接在頁(yè)面上顯示錯(cuò)誤信息,這樣對(duì)于前端來(lái)說(shuō)不是很好處理,我們將所有接口按照一定的格式返回,會(huì)方便前端交互處理。

未登錄

/**
 * 當(dāng)未登錄或者token失效訪問接口時(shí),自定義的返回結(jié)果
 *
 * @author harry
 * @公眾號(hào) Harry技術(shù)
 */
@Component
public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint {

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException {
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json");
        response.getWriter().println(JSONUtil.toJsonStr(R.unauthorized(authException.getMessage())));

        response.getWriter().flush();
    }

}

未授權(quán)

/**
 * 當(dāng)訪問接口沒有權(quán)限時(shí),自定義的返回結(jié)果
 *
 * @author harry
 * @公眾號(hào) Harry技術(shù)
 */
@Component
public class RestfulAccessDeniedHandler implements AccessDeniedHandler {

    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException e) throws IOException {
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json");
        response.getWriter().println(JSONUtil.toJsonStr(R.forbidden(e.getMessage())));
        response.getWriter().flush();
    }

}

創(chuàng)建JWT過濾器

這里直接使用了Hutool-jwt提供的JWTUtil工具類,主要包括:JWT創(chuàng)建、JWT解析、JWT驗(yàn)證。

/**
 * JWT登錄授權(quán)過濾器
 *
 * @author harry
 * @公眾號(hào) Harry技術(shù)
 */
@Slf4j
public class JwtValidationFilter extends OncePerRequestFilter {

    private final UserDetailsService userDetailsService;

    // 密鑰
    private final byte[] secretKey;

    public JwtValidationFilter(UserDetailsService userDetailsService, String secretKey) {
        this.userDetailsService = userDetailsService;
        this.secretKey = secretKey.getBytes();
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, @Nonnull HttpServletResponse response, @Nonnull FilterChain chain) throws ServletException, IOException {
        // 獲取請(qǐng)求token
        String token = request.getHeader(HttpHeaders.AUTHORIZATION);
        try {
            // 如果請(qǐng)求頭中沒有Authorization信息,或者Authorization以Bearer開頭,則認(rèn)為是匿名用戶
            if (StrUtil.isBlank(token) || !token.startsWith(SecurityConstants.JWT_TOKEN_PREFIX)) {
                chain.doFilter(request, response);
                return;
            }

            // 去除 Bearer 前綴
            token = token.substring(SecurityConstants.JWT_TOKEN_PREFIX.length());
            // 解析 Token
            JWT jwt = JWTUtil.parseToken(token);

            // 檢查 Token 是否有效(驗(yàn)簽 + 是否過期)
            boolean isValidate = jwt.setKey(secretKey).validate(0);
            if (!isValidate) {
                log.error("JwtValidationFilter error: token is invalid");
                throw new ApiException(ResultCode.UNAUTHORIZED);
            }
            JSONObject payloads = jwt.getPayloads();
            String username = payloads.getStr(JWTPayload.SUBJECT);
            SysUserDetails userDetails = (SysUserDetails) this.userDetailsService.loadUserByUsername(username);

            UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
            authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
            SecurityContextHolder.getContext().setAuthentication(authentication);

        } catch (Exception e) {
            log.error("JwtValidationFilter error: {}", e.getMessage());
            SecurityContextHolder.clearContext();
            throw new ApiException(ResultCode.UNAUTHORIZED);
        }
        // Token有效或無(wú)Token時(shí)繼續(xù)執(zhí)行過濾鏈
        chain.doFilter(request, response);
    }
}

改寫SecurityConfig

關(guān)于Spring Boot 3 集成 Spring Security相關(guān)的知識(shí)點(diǎn),可以參考文章:《Spring Boot 3 集成 Spring Security(1)認(rèn)證》、《Spring Boot 3 集成 Spring Security(2)授權(quán)》、《Spring Boot 3 集成 Spring Security(3)數(shù)據(jù)管理》。

/**
 * Spring Security 權(quán)限配置
 *
 * @author harry
 * @公眾號(hào) Harry技術(shù)
 */
@Configuration
@EnableWebSecurity
@EnableMethodSecurity(securedEnabled = true) // 開啟方法級(jí)別的權(quán)限控制
@RequiredArgsConstructor
public class SecurityConfig {

    private final RestfulAccessDeniedHandler restfulAccessDeniedHandler;
    private final RestAuthenticationEntryPoint restAuthenticationEntryPoint;
    private final SecurityProperties securityProperties;
    private final UserDetailsService userDetailsService;

    @Bean
    protected SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {

        // 忽略的路徑
        http.authorizeHttpRequests(requestMatcherRegistry -> requestMatcherRegistry.requestMatchers(
                        securityProperties.getIgnoreUrls().toArray(new String[0])).permitAll()
                .anyRequest().authenticated()
        );

        http
                // 由于使用的是JWT,我們這里不需要csrf
                .csrf(AbstractHttpConfigurer::disable)
                // 禁用session
                .sessionManagement(configurer ->
                        configurer
                                .sessionCreationPolicy(SessionCreationPolicy.STATELESS));

        // 添加自定義未授權(quán)和未登錄結(jié)果返回
        http.exceptionHandling(customizer ->
                customizer
                        // 處理未授權(quán)
                        .accessDeniedHandler(restfulAccessDeniedHandler)
                        // 處理未登錄
                        .authenticationEntryPoint(restAuthenticationEntryPoint));
        // JWT 校驗(yàn)過濾器
        http.addFilterBefore(new JwtValidationFilter(userDetailsService, securityProperties.getJwt().getKey()), UsernamePasswordAuthenticationFilter.class);

        return http.build();
    }

    /**
     * AuthenticationManager 手動(dòng)注入
     *
     * @param authenticationConfiguration 認(rèn)證配置
     */
    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
        return authenticationConfiguration.getAuthenticationManager();
    }

    /**
     * 強(qiáng)散列哈希加密實(shí)現(xiàn)
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

這里主要做了以下幾點(diǎn)配置:

  • 將不需要認(rèn)證鑒權(quán)的接口加入白名單
  • 由于使用的是JWT,我們這里不需要csrf、禁用session
  • 添加自定義未授權(quán)和未登錄結(jié)果返回
  • 配置 JWT 校驗(yàn)過濾器

我們根據(jù)數(shù)據(jù)庫(kù)中的用戶信息加載用戶,并將角色轉(zhuǎn)換為 Spring Security 能識(shí)別的格式。我們寫一個(gè)SysUserDetails類來(lái)實(shí)現(xiàn)自定義Spring Security 用戶對(duì)象。

/**
 * 用戶詳情服務(wù)
 *
 * @author harry
 * @公眾號(hào) Harry技術(shù)
 */
@Service
@RequiredArgsConstructor
public class UserDetailsServiceImpl implements UserDetailsService {

    private final SysUserMapper sysUserMapper;
    private final SysMenuMapper sysMenuMapper;
    private final SysUserRoleMapper sysUserRoleMapper;

    @Override
    @Cacheable(value = CacheConstants.USER_DETAILS, key = "#username", unless = "#result == null ")
    public SysUserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        // 獲取登錄用戶信息
        SysUser user = sysUserMapper.selectByUsername(username);

        // 用戶不存在
        if (BeanUtil.isEmpty(user)) {
            throw new ApiException(SysExceptionEnum.USER_NOT_EXIST);
        }
        Long userId = user.getUserId();

        // 用戶停用
        if (StatusEnums.DISABLE.getKey().equals(user.getStatus())) {
            throw new ApiException(SysExceptionEnum.USER_DISABLED);
        }

        // 獲取角色
        Set<String> roles = sysUserRoleMapper.listRoleKeyByUserId(userId);

        // 獲取數(shù)據(jù)范圍標(biāo)識(shí)
        Integer dataScope = sysUserRoleMapper.getMaximumDataScope(roles);

        Set<String> permissions = new HashSet<>();
        // 如果 roles 包含 root 則擁有所有權(quán)限
        if (roles.contains(CommonConstant.SUPER_ADMIN_ROOT)) {
            permissions.add(CommonConstant.ALL_PERMISSION);
        } else {
            // 獲取菜單權(quán)限標(biāo)識(shí)
            permissions = sysMenuMapper.getMenuPermission(userId);
            // 過濾空字符串
            permissions.remove("");
        }

        return new SysUserDetails(user, permissions, roles, username, dataScope);
    }

}

這里使用了@Cacheable結(jié)合redis做的緩存處理,關(guān)于緩存相關(guān)配置,可以參考文章《Spring Boot 3 整合Redis(1) 基礎(chǔ)功能》、《Spring Boot 3 整合Redis(2)注解驅(qū)動(dòng)緩存》。

登錄驗(yàn)證

寫一個(gè)登錄接口/auth/login,返回 token、tokenType等信息

/**
 * 登錄相關(guān)
 *
 * @author harry
 * @公眾號(hào) Harry技術(shù)
 */
@Slf4j
@RestController
@RequiredArgsConstructor
@Tag(name = "認(rèn)證中心")
@RequestMapping("/auth")
public class LoginController {

    private final SysUserService sysUserService;

    @Operation(summary = "login 登錄")
    @PostMapping(value = "/login")
    public R<LoginResult> login(@RequestBody SysUserLoginParam sysUserLoginParam) {

        return R.success(sysUserService.login(sysUserLoginParam.getUsername(), sysUserLoginParam.getPassword()));
    }

    @Operation(summary = "info 獲取當(dāng)前用戶信息")
    @GetMapping(value = "/info")
    public R<UserInfoResult> getInfo() {
        UserInfoResult result = sysUserService.getInfo();
        return R.success(result);
    }

    @Operation(summary = "logout 注銷")
    @PostMapping(value = "/logout")
    public R logout(HttpServletRequest request) {
        // 需要 將當(dāng)前用戶token 設(shè)置無(wú)效
        SecurityContextHolder.clearContext();
        return R.success();
    }

}

LoginResult 對(duì)象

/**
 *
 * @author harry
 * @公眾號(hào) Harry技術(shù)
 */
@Data
public class LoginResult {

    @Schema(description = "token")
    private String token;

    @Schema(description = "token 類型", example = "Bearer")
    private String tokenType;

    @Schema(description = "過期時(shí)間(單位:秒)", example = "604800")
    private Long expiration;

    @Schema(description = "刷新token")
    private String refreshToken;

}

啟動(dòng)查看接口

訪問http://localhost:8080/swagger-ui/index.html或者http://localhost:8080/doc.html

未登錄

當(dāng)我們處于未登錄狀態(tài)時(shí)訪問/auth/info接口,直接返回了我們自定義的異常信息

登錄

這里我們登錄用戶 harry/123456,設(shè)定用戶角色TEST,菜單權(quán)限不給字典相關(guān)的操作。

看到接口成功返回token等信息,我們將token信息填寫到 Authorize,作為全局配置。

這時(shí),我們?cè)L問/auth/info,可以看到當(dāng)前登錄的用戶信息

我們?cè)L問字典相關(guān)的接口,如:/sys_dict/page,返回了沒有相關(guān)權(quán)限的信息

訪問其他接口,如:/sys_dept/page,可以看到數(shù)據(jù)正常返回。

總結(jié)

到這里,我們已經(jīng)掌握了Spring Boot 3 整合 Security 的全過程。我們將從簡(jiǎn)單的入門開始,然后學(xué)習(xí)如何整合數(shù)據(jù)庫(kù),并最終使用 JWT 實(shí)現(xiàn)前后端分離。這些知識(shí)將幫助我們構(gòu)建更安全、更可靠的應(yīng)用程序。后續(xù)我們會(huì)深入了解在項(xiàng)目中用到的一些其他框架、工具。讓我們一起開始吧!

到此這篇關(guān)于SpringBoot3集成SpringSecurity+JWT的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)SpringBoot3集成SpringSecurity+JWT內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • springboot集成flyway全過程

    springboot集成flyway全過程

    這篇文章主要介紹了springboot集成flyway全過程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2025-06-06
  • Java AOP動(dòng)態(tài)代理詳細(xì)介紹

    Java AOP動(dòng)態(tài)代理詳細(xì)介紹

    AOP是一種設(shè)計(jì)思想,是軟件設(shè)計(jì)領(lǐng)域中的面向切面編程,它是面向?qū)ο缶幊痰囊环N補(bǔ)充和完善。本文將用Java實(shí)現(xiàn)AOP代理的三種方式,需要的可以參考一下
    2022-08-08
  • springboot中@RequestMapping的用法

    springboot中@RequestMapping的用法

    這篇文章主要介紹了springboot中@RequestMapping的用法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-02-02
  • Eureka注冊(cè)不上或注冊(cè)后IP不對(duì)(多網(wǎng)卡的坑及解決)

    Eureka注冊(cè)不上或注冊(cè)后IP不對(duì)(多網(wǎng)卡的坑及解決)

    這篇文章主要介紹了Eureka注冊(cè)不上或注冊(cè)后IP不對(duì)(多網(wǎng)卡的坑及解決),具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-11-11
  • Spring如何使用@Indexed加快啟動(dòng)速度

    Spring如何使用@Indexed加快啟動(dòng)速度

    這篇文章主要介紹了Spring如何使用@Indexed加快啟動(dòng)速度,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • Springboot2集成pagehelper過程圖解

    Springboot2集成pagehelper過程圖解

    這篇文章主要介紹了springboot2集成pagehelper過程圖解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-03-03
  • 據(jù)說(shuō)這個(gè)是可以擼到2089年的idea2020.2(推薦)

    據(jù)說(shuō)這個(gè)是可以擼到2089年的idea2020.2(推薦)

    這篇文章主要介紹了據(jù)說(shuō)這個(gè)是可以擼到2089年的idea2020.2,本教程給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-09-09
  • SpringBoot使用Graylog日志收集的實(shí)現(xiàn)示例

    SpringBoot使用Graylog日志收集的實(shí)現(xiàn)示例

    Graylog是一個(gè)生產(chǎn)級(jí)別的日志收集系統(tǒng),集成Mongo和Elasticsearch進(jìn)行日志收集,這篇文章主要介紹了SpringBoot使用Graylog日志收集的實(shí)現(xiàn)示例,感興趣的小伙伴們可以參考一下
    2019-04-04
  • java實(shí)現(xiàn)圖書管理系統(tǒng)

    java實(shí)現(xiàn)圖書管理系統(tǒng)

    這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)圖書管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-03-03
  • mybatis注解開發(fā)使用foreach方式

    mybatis注解開發(fā)使用foreach方式

    這篇文章主要介紹了mybatis注解開發(fā)使用foreach方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-08-08

最新評(píng)論