最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

SpringSecurity和jwt實(shí)現(xiàn)登錄及權(quán)限認(rèn)證功能

 更新時(shí)間:2025年11月27日 09:26:58   作者:q***7219  
本文介紹了如何使用SpringSecurity和JWT實(shí)現(xiàn)系統(tǒng)的登錄和權(quán)限控制,包括JWT的工作原理、RBAC角色權(quán)限管理、數(shù)據(jù)庫(kù)設(shè)計(jì)、攔截器實(shí)現(xiàn)、登錄接口和權(quán)限驗(yàn)證的代碼示例,感興趣的朋友跟隨小編一起看看吧

系列文章目錄

spring security+jwt安全方案

前言

前面我們已經(jīng)通過(guò)使用springboot框架獲得了管理數(shù)據(jù)的基本能力,但是一個(gè)系統(tǒng)不和或缺的功能是安全登錄。
這里我們以springsecurity+jwt方案實(shí)現(xiàn)登錄以及權(quán)限控制。

一、springsecurity+jwt方案

提示:這里是對(duì)該方案的原理簡(jiǎn)介
一個(gè)安全的系統(tǒng)是需要對(duì)請(qǐng)求身份進(jìn)行認(rèn)證的。

但是http協(xié)議是無(wú)狀態(tài)的,所以需要對(duì)每次的請(qǐng)求進(jìn)行校驗(yàn)。

以下是jwt方案流程圖,我們以現(xiàn)實(shí)生活為例。當(dāng)我們被一個(gè)學(xué)校錄取,我們?cè)陂_(kāi)學(xué)的時(shí)候需要提供身份證(類(lèi)比賬號(hào)密碼),學(xué)校就會(huì)發(fā)放一個(gè)學(xué)生證(類(lèi)比jwt令牌),這樣我們每次進(jìn)學(xué)校帶學(xué)生證就行了(每次使用系統(tǒng)帶jwt就行了)

二、權(quán)限控制RBAC

提示:這里是對(duì)登錄的細(xì)化,即權(quán)限功能
登錄系統(tǒng)的人并不只是一個(gè)人,以下為RBAC的數(shù)據(jù)庫(kù)設(shè)計(jì)圖。

我們依然以現(xiàn)實(shí)世界為例,一個(gè)教務(wù)系統(tǒng),有很多用戶(hù)(user);其中有兩種身份(role):老師和學(xué)生;老師和學(xué)生擁有不一樣的功能(menu),老師可以改卷子打分等等。

三、實(shí)現(xiàn)

我們以該圖為例,該流程即為需要實(shí)現(xiàn)的。

1.RBAC數(shù)據(jù)庫(kù)實(shí)現(xiàn)

這里請(qǐng)自行搜索RBAC的sql代碼

2.攔截器實(shí)現(xiàn)

給系統(tǒng)套上一層攔截功能即是security實(shí)現(xiàn)的功能,這里先實(shí)現(xiàn)接口放行
在maven添加以下依賴(lài)后:

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

系統(tǒng)會(huì)自動(dòng)生成一個(gè)登錄頁(yè)面,我們需要做的就是給登錄接口放行,其他接口攔截的配置
參考以下配置

package com.nie.sportserver.config;
import com.nie.sportserver.Interceptor.JwtTokenAdminInterceptor;
import com.nie.sportserver.exception.MyAccessDeniedHandler;
import com.nie.sportserver.exception.MyAuthenticationEntryPoint;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
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.WebSecurityConfigurerAdapter;
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.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
import java.time.Duration;
import java.util.Arrays;
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    MyAccessDeniedHandler myAccessDeniedHandler;
    @Autowired
    MyAuthenticationEntryPoint myAuthenticationEntryPoint;
    @Autowired
    JwtTokenAdminInterceptor jwtTokenAdminInterceptor;
    //加密算法
    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }
    //security配置跨域
    @Bean
    public CorsFilter corsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        corsConfiguration.addAllowedOriginPattern("*");
        corsConfiguration.addAllowedHeader("*");
        corsConfiguration.addAllowedMethod("*");
        corsConfiguration.setAllowCredentials(true);
        source.registerCorsConfiguration("/**", corsConfiguration);
        return new CorsFilter(source);
    }
    //配置安全攔截
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.cors().and().csrf().disable()//關(guān)閉csrf
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                //不通過(guò)Session獲取Securitycontext
                .and()//配置異常處理
                .exceptionHandling()
                .authenticationEntryPoint(myAuthenticationEntryPoint)
                .accessDeniedHandler(myAccessDeniedHandler)
                .and()
                .authorizeRequests()
                //接口匿名訪(fǎng)問(wèn)
                .antMatchers("/doc.html",
                        "/favicon.ico",
                        "/v2/api-docs",
                        "/swagger-resources/**",
                        "/webjars/**","/user/login").anonymous()//攜帶token了就無(wú)法訪(fǎng)問(wèn)了
                .anyRequest().authenticated();
        http.addFilterBefore(jwtTokenAdminInterceptor, UsernamePasswordAuthenticationFilter.class);
    }
    //暴露認(rèn)證方法變?yōu)閎ean對(duì)象
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
}

關(guān)鍵在于以下代碼

                .antMatchers("/doc.html",
                        "/favicon.ico",
                        "/v2/api-docs",
                        "/swagger-resources/**",
                        "/webjars/**","/user/login").anonymous()

3.登錄接口實(shí)現(xiàn)

由于之前已經(jīng)實(shí)現(xiàn)了放行,我們只需要完成查詢(xún)數(shù)據(jù)庫(kù),并且將數(shù)據(jù)生成jwt即可
在上面的配置中,我們已經(jīng)把認(rèn)證方法暴露為bean對(duì)象,我們實(shí)現(xiàn)該方法即可

    //暴露認(rèn)證方法變?yōu)閎ean對(duì)象
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

示例如下

package com.nie.sportserver.service.impl;
import com.nie.sportpojo.entity.LoginUser;
import com.nie.sportpojo.entity.User;
import com.nie.sportserver.mapper.LoginMapper;
import com.nie.sportserver.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@Service
public class UserDetailServiceImpl implements UserDetailsService {
    @Autowired
    private LoginMapper loginMapper;
    @Autowired
    private UserMapper userMapper;
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //查詢(xún)用戶(hù)信息
        User user = loginMapper.getByUserName(username);
        //把數(shù)據(jù)封裝為UserDetail返回
        //todo 查詢(xún)對(duì)應(yīng)的權(quán)限信息
        List<String> list = new ArrayList<>(userMapper.selectPermsByUserId(user.getId()));
        LoginUser loginUser = new LoginUser(user,list);
        return loginUser;
    }
}

4.攔截器實(shí)現(xiàn)

在前面的配置中,我們已經(jīng)將普通請(qǐng)求攔截了,并且使用攔截器

    @Autowired
    JwtTokenAdminInterceptor jwtTokenAdminInterceptor;

這里來(lái)實(shí)現(xiàn)攔截器

package com.nie.sportserver.Interceptor;
import com.nie.sportcommon.utills.JwtUtil;
import com.nie.sportpojo.entity.LoginUser;
import com.nie.sportpojo.entity.User;
import com.nie.sportserver.mapper.LoginMapper;
import com.nie.sportserver.mapper.UserMapper;
import com.nie.sportserver.properties.JwtProperties;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@Component
@Slf4j
public class JwtTokenAdminInterceptor extends OncePerRequestFilter {
    @Autowired
    private JwtProperties jwtProperties;
    @Autowired
    private LoginMapper loginMapper;
    @Autowired
    private UserMapper userMapper;
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String requestURI = request.getRequestURI();
        //從請(qǐng)求頭中獲取令牌
        String token = request.getHeader(jwtProperties.getAdminTokenName());
        if (!StringUtils.hasText((token))) {
            filterChain.doFilter(request, response);
            return;
        }
        //校驗(yàn)令牌
        Long userId;
        try {
            log.info("jwt校驗(yàn){}", token);
            //token解析
            Claims claims = JwtUtil.parseJWT(jwtProperties.getAdminSecretKey(), token);
            userId = Long.valueOf(claims.get("userId").toString());
            log.info("當(dāng)前用戶(hù)id:{}", userId);
        } catch (Exception ex) {
            throw new RuntimeException("token非法");
        }
        User user = loginMapper.getByUserId(userId);
        List<String> list = new ArrayList<>(userMapper.selectPermsByUserId(user.getId()));
        LoginUser loginUser = new LoginUser(user,list);
        UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(loginUser,null,loginUser.getAuthorities());
        SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
        //放行
        filterChain.doFilter(request, response);
    }
}

其他工具類(lèi)

jwt依賴(lài)

        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.1</version>
        </dependency>

jwt工具類(lèi)

package com.nie.sportcommon.utills;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.Map;
public class JwtUtil {
    /**
     * 生成jwt
     * 使用Hs256算法, 私匙使用固定秘鑰
     *
     * @param secretKey jwt秘鑰
     * @param ttlMillis jwt過(guò)期時(shí)間(毫秒)
     * @param claims    設(shè)置的信息
     * @return
     */
    public static String createJWT(String secretKey, long ttlMillis, Map<String, Object> claims) {
        // 指定簽名的時(shí)候使用的簽名算法,也就是header那部分
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
        // 生成JWT的時(shí)間
        long expMillis = System.currentTimeMillis() + ttlMillis;
        Date exp = new Date(expMillis);
        // 設(shè)置jwt的body
        JwtBuilder builder = Jwts.builder()
                // 如果有私有聲明,一定要先設(shè)置這個(gè)自己創(chuàng)建的私有的聲明,這個(gè)是給builder的claim賦值,一旦寫(xiě)在標(biāo)準(zhǔn)的聲明賦值之后,就是覆蓋了那些標(biāo)準(zhǔn)的聲明的
                .setClaims(claims)
                // 設(shè)置簽名使用的簽名算法和簽名使用的秘鑰
                .signWith(signatureAlgorithm, secretKey.getBytes(StandardCharsets.UTF_8))
                // 設(shè)置過(guò)期時(shí)間
                .setExpiration(exp);
        return builder.compact();
    }
    /**
     * Token解密
     *
     * @param secretKey jwt秘鑰 此秘鑰一定要保留好在服務(wù)端, 不能暴露出去, 否則sign就可以被偽造, 如果對(duì)接多個(gè)客戶(hù)端建議改造成多個(gè)
     * @param token     加密后的token
     * @return
     */
    public static Claims parseJWT(String secretKey, String token) {
        // 得到DefaultJwtParser
        Claims claims = Jwts.parser()
                // 設(shè)置簽名的秘鑰
                .setSigningKey(secretKey.getBytes(StandardCharsets.UTF_8))
                // 設(shè)置需要解析的jwt
                .parseClaimsJws(token).getBody();
        return claims;
    }
}

權(quán)限

權(quán)限認(rèn)證使用 @PreAuthorize(“hasAuthority(‘’)”)注解

總結(jié)

本文對(duì)jwt登錄校驗(yàn),權(quán)限管理的原理簡(jiǎn)單描述,并且提供了實(shí)現(xiàn)方案

到此這篇關(guān)于SpringSecurity+jwt實(shí)現(xiàn)登錄及權(quán)限認(rèn)證功能的文章就介紹到這了,更多相關(guān)SpringSecurity jwt權(quán)限認(rèn)證內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java程序設(shè)計(jì)之12個(gè)經(jīng)典樣例

    Java程序設(shè)計(jì)之12個(gè)經(jīng)典樣例

    這篇文章主要給大家分享關(guān)于Java程序設(shè)計(jì)11個(gè)經(jīng)典樣例,主要以舉例的形式詳細(xì)的講解了Java程序設(shè)計(jì)的各種方法,需要的朋友可以參考一下文章具體的內(nèi)容
    2021-10-10
  • java中參數(shù)傳遞方式詳解

    java中參數(shù)傳遞方式詳解

    這篇文章主要介紹了java中參數(shù)傳遞方式詳解的相關(guān)資料,需要的朋友可以參考下
    2017-03-03
  • Java如何輸出windows中的全部漢字

    Java如何輸出windows中的全部漢字

    這篇文章主要介紹了Java如何輸出windows中的全部漢字問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-01-01
  • SpringBoot打包成Docker鏡像的幾種實(shí)現(xiàn)方式

    SpringBoot打包成Docker鏡像的幾種實(shí)現(xiàn)方式

    Spring Boot是一個(gè)用于構(gòu)建獨(dú)立的、可執(zhí)行的Spring應(yīng)用程序的框架,結(jié)合使用Spring Boot和Docker,可以方便地將應(yīng)用程序部署到不同的環(huán)境中本文,主要介紹了SpringBoot打包成Docker鏡像的幾種實(shí)現(xiàn)方式,感興趣的可以了解一下
    2024-01-01
  • Spring boot攔截器實(shí)現(xiàn)IP黑名單的完整步驟

    Spring boot攔截器實(shí)現(xiàn)IP黑名單的完整步驟

    這篇文章主要給大家介紹了關(guān)于Spring boot攔截器實(shí)現(xiàn)IP黑名單的完整步驟,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用Spring boot攔截器具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-06-06
  • IDEA搭建配置Java?Web項(xiàng)目的詳細(xì)步驟

    IDEA搭建配置Java?Web項(xiàng)目的詳細(xì)步驟

    這篇文章詳細(xì)介紹了如何使用IDEA創(chuàng)建和配置JavaWeb項(xiàng)目,包括項(xiàng)目結(jié)構(gòu)設(shè)置、WEB-INF目錄和jsp文件的創(chuàng)建,以及Tomcat的配置,是Java初學(xué)者的實(shí)用指南,需要的朋友可以參考下
    2024-10-10
  • MyBatis-Plus實(shí)現(xiàn)公共字段自動(dòng)填充功能詳解

    MyBatis-Plus實(shí)現(xiàn)公共字段自動(dòng)填充功能詳解

    在開(kāi)發(fā)中經(jīng)常遇到多個(gè)實(shí)體類(lèi)有共同的屬性字段,這些字段屬于公共字段,也就是很多表中都有這些字段,能不能對(duì)于這些公共字段在某個(gè)地方統(tǒng)一處理,來(lái)簡(jiǎn)化開(kāi)發(fā)呢?MyBatis-Plus就提供了這一功能,本文就來(lái)為大家詳細(xì)講講
    2022-08-08
  • 使用proguard對(duì)maven構(gòu)建的springboot項(xiàng)目進(jìn)行混淆方式

    使用proguard對(duì)maven構(gòu)建的springboot項(xiàng)目進(jìn)行混淆方式

    文章介紹了如何使用ProGuard對(duì)Maven構(gòu)建的Spring Boot項(xiàng)目進(jìn)行混淆,并解決混淆后可能遇到的版本兼容性問(wèn)題和類(lèi)名沖突問(wèn)題,主要步驟包括下載高版本的ProGuard、配置POM文件、添加ProGuard配置文件、修改Spring Boot啟動(dòng)文件以避免類(lèi)名沖突
    2024-11-11
  • Spring構(gòu)造器注入及@Autowired、lombok的@RequiredArgsConstructor的異同點(diǎn)說(shuō)明

    Spring構(gòu)造器注入及@Autowired、lombok的@RequiredArgsConstructor的異同點(diǎn)說(shuō)明

    本文介紹了Spring框架中構(gòu)造器注入的實(shí)現(xiàn)原理,包括Spring如何找到參數(shù)、構(gòu)造器注入與Autowired的區(qū)別,以及為什么Spring官方推薦構(gòu)造器注入,文章還強(qiáng)調(diào)了構(gòu)造器注入中使用final關(guān)鍵字的好處,并提供了代碼示例,幫助讀者更好地理解和應(yīng)用Spring的最佳實(shí)踐
    2026-03-03
  • Opencv實(shí)現(xiàn)身份證OCR識(shí)別的示例詳解

    Opencv實(shí)現(xiàn)身份證OCR識(shí)別的示例詳解

    這篇文章主要為大家詳細(xì)介紹了如何使用Opencv實(shí)現(xiàn)身份證OCR識(shí)別功能,文中的示例代碼講解詳細(xì),具有一定的學(xué)習(xí)價(jià)值,感興趣的小伙伴可以跟隨小編一起了解一下
    2024-03-03

最新評(píng)論

仁布县| 广东省| 南召县| 嘉黎县| 沁水县| 寿阳县| 阿荣旗| 贵溪市| 宜昌市| 英山县| 盐津县| 宣威市| 蓬溪县| 竹溪县| 东源县| 营口市| 沙湾县| 黄梅县| 朝阳县| 旬阳县| 玉林市| 芜湖市| 四子王旗| 莆田市| 景德镇市| 明光市| 漳平市| 武强县| 沈丘县| 盖州市| 林州市| 昆山市| 普宁市| 玛多县| 辽中县| 新丰县| 华亭县| 营口市| 山东省| 镇平县| 隆安县|