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

SpringSecurity詳解整合JWT實(shí)現(xiàn)全過程

 更新時(shí)間:2022年07月26日 09:41:25   作者:kaico2018  
JWT作為一個(gè)開放的標(biāo)準(zhǔn)(?RFC?7519?),定義了一種簡潔的,自包含的方法用于通信雙方之間以Json對象的形式安全的傳遞信息。接下來通過本文給大家介紹springSecurity+jwt實(shí)現(xiàn)互踢功能,需要的朋友可以參考下

Token

Token和Sessionid的思想一樣。Session是存在服務(wù)器端JVM中,Token存在Redis中。

解決分布式Session數(shù)據(jù)一致性問題:Spring-Session

傳統(tǒng)的Token,例如:用戶登錄成功生成對應(yīng)的令牌,key:為令牌, value:userid,隱藏了數(shù)據(jù)真實(shí)性 ,同時(shí)將該token存放到redis中,返回對應(yīng)的真實(shí)令牌給客戶端存放。客戶端每次訪問后端請求的時(shí)候,會(huì)傳遞該token在請求中,服務(wù)器端接收到該token之后,從redis中查詢?nèi)绻嬖诘那闆r下,則說明在有效期內(nèi),如果在Redis中不存在的情況下,則說明過期或者token錯(cuò)誤。

Token使用:

  1. 驗(yàn)證賬號密碼成功
  2. 生成一個(gè)令牌UUID
  3. 將該令牌存放到redis中,key為令牌,value值對應(yīng)存放userid
  4. 最終返回令牌給客戶端

Token驗(yàn)證回話信息

  1. 在請求頭中傳遞該令牌
  2. 從Redis中驗(yàn)證該令牌是否有效期
  3. 獲取value內(nèi)容
  4. 根據(jù)userid查詢用戶信息,返回給客戶端

Token存放數(shù)據(jù)優(yōu)缺點(diǎn):

缺點(diǎn):

  1. 必須依賴服務(wù)器,占用服務(wù)器端資源
  2. 效率非常低

優(yōu)點(diǎn):

  1. 可以隱藏?cái)?shù)據(jù)真實(shí)性
  2. 適用于分布式/微服務(wù)
  3. 安全性高 JWT

Jwt

JSON WEB Token JWT的聲明一般被用來在身份提供者和服務(wù)提供者間傳遞被認(rèn)證的用戶身份信息,以便于從資源服務(wù)器獲取資源,也可以增加一些額外的其它業(yè)務(wù)邏輯所必須的聲明信息,該token也可直接被用于認(rèn)證,也可被加密。

組成

第一部分:header (頭部)

描述加密算法

HS256:屬于驗(yàn)證簽名

RSA256:屬于非對稱加密

第二部分:playload(載荷)

攜帶存放的數(shù)據(jù) 用戶名稱、用戶頭像之類,需要注意銘感數(shù)據(jù)

標(biāo)準(zhǔn)中注冊的聲明 (建議但不強(qiáng)制使用) :

  • iss: jwt簽發(fā)者
  • sub: jwt所面向的用戶
  • aud: 接收jwt的一方
  • exp: jwt的過期時(shí)間,這個(gè)過期時(shí)間必須要大于簽發(fā)時(shí)間
  • nbf: 定義在什么時(shí)間之前,該jwt都是不可用的.
  • iat: jwt的簽發(fā)時(shí)間
  • jti: jwt的唯一身份標(biāo)識(shí),主要用來作為一次性token,從而回避重放攻擊。

第三部分:secret (存放在服務(wù)器端)

簽名值,Base64(header .playload) +秘鑰

JWT和Token的區(qū)別

1、token對應(yīng)的數(shù)據(jù)存放在redis中

2、JWT對應(yīng)存放的數(shù)據(jù)(payload中)客戶端

優(yōu)缺點(diǎn)

優(yōu)點(diǎn):

1、JWT數(shù)據(jù)存放在客戶端,不依賴于服務(wù)器端,減輕服務(wù)器端壓力

2、效率比傳統(tǒng)的token驗(yàn)證還要高

缺點(diǎn)

1、jwt一旦生成之后后期無法修改

2、無法銷毀一個(gè)jwt

3、建議不要放敏感數(shù)據(jù),userid、手機(jī)號

4、后端無法統(tǒng)計(jì) 生成JWT

手寫JWT

public class Test001 {
    private static final String SIGN_KEY = "kaicoSignKey";
    public static void main(String[] args) throws UnsupportedEncodingException {
        //手寫jwt 封裝三個(gè)部分:header、payload、sign簽名
        //定義header
        JSONObject header = new JSONObject();
        header.put("alg", "HS256");
        //payload
        JSONObject payload = new JSONObject();
        payload.put("name", "kaico");
        String headerEncode = Base64.getEncoder().encodeToString(header.toJSONString().getBytes());
        String payloadJSONString = payload.toJSONString();
        String payloadEncode = Base64.getEncoder().encodeToString(payloadJSONString.getBytes());
        //sign簽名值 實(shí)際上就是 md5
        String sign = DigestUtils.md5DigestAsHex((payload + SIGN_KEY).getBytes());
        String jwt = headerEncode + "." + payloadEncode + "." + sign;
        System.out.println(jwt);
        //解密
        String payloadEncodeStr = jwt.split("\\.")[1];
        String payloadDecoder = new String(Base64.getDecoder().decode(payloadEncodeStr), "UTF-8");
        String newSign = DigestUtils.md5DigestAsHex((payloadDecoder + SIGN_KEY).getBytes());
        System.out.println(newSign.equals(jwt.split("\\.")[2]));
    }
}

使用工具類新建JWT

public class Test003 {
    private static final String SIGN_KEY = "kaicoSignKey";
    public static void main(String[] args) {
        long now = System.currentTimeMillis();
        //設(shè)置過期時(shí)間 (測試使用1秒鐘)
        Long exp = now + 1 * 1000;
        JwtBuilder jwtBuilder = Jwts.builder()
                //payload值
                .claim("userImg", "sssss")
                //簽名值
                .signWith(SignatureAlgorithm.HS256, SIGN_KEY)
                .setExpiration(new Date(exp));

        //輸出JWT的內(nèi)容
        String jwt = jwtBuilder.compact();
        System.out.println(jwt);
        //解密
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        Claims body = Jwts.parser().setSigningKey(SIGN_KEY).parseClaimsJws(jwt).getBody();
        System.out.println(body.get("userImg"));
    }
}

Springboot整合JWT

登錄流程

1、驗(yàn)證賬號密碼

2、賬號密碼驗(yàn)證成功,生成JWT返回給客戶端(移動(dòng)app、瀏覽器、微信小程序)

3、客戶端請求服務(wù)端,服務(wù)端驗(yàn)證JWT

    1. base64解密jwt獲取payload中的數(shù)據(jù)
    2. 獲取roles權(quán)限列表注冊到SpringSecurity框架中

代碼整合

在上次整合SpringSecurity的基礎(chǔ)上

新增兩個(gè)過濾器

package com.kaico.jwt.filter;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.kaico.jwt.entity.UserEntity;
import com.kaico.jwt.utils.JwtUtils;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
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;
/**
 * @Author kaico
 * @Description //TODO
 * @Date 19:26 2022/7/25
 */
public class JWTLoginFilter extends UsernamePasswordAuthenticationFilter {
    /**
     * 獲取授權(quán)管理
     */
    private AuthenticationManager authenticationManager;
    public JWTLoginFilter(AuthenticationManager authenticationManager) {
        this.authenticationManager = authenticationManager;
        /**
         *  后端登陸接口
         */
        super.setFilterProcessesUrl("/auth/login");
    }
    @Override
    public Authentication attemptAuthentication(HttpServletRequest req,
                                                HttpServletResponse res) {
        try {
            UserEntity user = new ObjectMapper()
                    .readValue(req.getInputStream(), UserEntity.class);
            return authenticationManager.authenticate(
                    new UsernamePasswordAuthenticationToken(
                            user.getUsername(),
                            user.getPassword(),
                            new ArrayList<>())
            );
        } catch (IOException e) {
            logger.error(e.getMessage());
            return  null;
        }
    }
    @Override
    /**
     * 用戶登陸成功之后驗(yàn)證
     */
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
                                            Authentication authResult) throws IOException, ServletException {
        UserEntity userEntity = (UserEntity) authResult.getPrincipal();
        String jwtToken = JwtUtils.generateJsonWebToken(userEntity);
        response.addHeader("token", jwtToken);
    }
    /**
     * 賬號或者密碼錯(cuò)誤
     * @param request
     * @param response
     * @param failed
     * @throws IOException
     * @throws ServletException
     */
    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
        response.setCharacterEncoding("UTF-8");
        response.getWriter().print("賬號或者密碼錯(cuò)誤");
    }
}
package com.kaico.jwt.filter;
import com.kaico.jwt.utils.JwtUtils;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
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.List;
/**
 * @Author kaico
 * @Description //TODO 
 * @Date 19:37 2022/7/25
 */
public class JWTValidationFilter extends BasicAuthenticationFilter {
    public JWTValidationFilter(AuthenticationManager authenticationManager) {
        super(authenticationManager);
    }
    /**
     * 過濾請求驗(yàn)證
     *
     * @param request
     * @param response
     * @param chain
     * @throws IOException
     * @throws ServletException
     */
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
        SecurityContextHolder.getContext().setAuthentication(setAuthentication(request.getHeader("token")));
        super.doFilterInternal(request, response, chain);
    }
    /**
     * 驗(yàn)證token 并且驗(yàn)證權(quán)限
     * @param token
     * @return
     */
    private UsernamePasswordAuthenticationToken setAuthentication(String token) {
        String username = JwtUtils.getUsername(token);
        if (username == null) {
            return null;
        }
        //解析權(quán)限列表
        List<SimpleGrantedAuthority> userRoleList = JwtUtils.getUserRole(token);
        return new UsernamePasswordAuthenticationToken(username, null, userRoleList);
    }
}

JWT工具類

package com.kaico.jwt.utils;
import com.alibaba.fastjson.JSONArray;
import com.kaico.jwt.entity.UserEntity;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class JwtUtils {
    public static final String TOKEN_HEADER = "token";
    public static final String TOKEN_PREFIX = "Bearer ";
    private static final String SUBJECT = "kaico";
    //JWT有效期
    private static final long EXPIRITION = 1000 * 24 * 60 * 60 * 7;
    private static final String APPSECRET_KEY = "kaico_secret";
    private static final String ROLE_CLAIMS = "roles";
    public static String generateJsonWebToken(UserEntity user) {
        String token = Jwts
                .builder()
                .setSubject(SUBJECT)
                .claim(ROLE_CLAIMS, user.getAuthorities())
                .claim("username", user.getUsername())
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + EXPIRITION))
                .signWith(SignatureAlgorithm.HS256, APPSECRET_KEY).compact();
        return token;
    }
    /**
     * 生成token
     *
     * @param username
     * @param role
     * @return
     */
    public static String createToken(String username, String role) {
        Map<String, Object> map = new HashMap<>();
        map.put(ROLE_CLAIMS, role);
        String token = Jwts
                .builder()
                .setSubject(username)
                .setClaims(map)
                .claim("username", username)
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + EXPIRITION))
                .signWith(SignatureAlgorithm.HS256, APPSECRET_KEY).compact();
        return token;
    }
    public static Claims checkJWT(String token) {
        try {
            final Claims claims = Jwts.parser().setSigningKey(APPSECRET_KEY).parseClaimsJws(token).getBody();
            return claims;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
    /**
     * 獲取用戶名
     *
     * @param token
     * @return
     */
    public static String getUsername(String token) {
        Claims claims = Jwts.parser().setSigningKey(APPSECRET_KEY).parseClaimsJws(token).getBody();
        return claims.get("username").toString();
    }
    /**
     * 獲取用戶角色
     *
     * @param token
     * @return
     */
    public static List<SimpleGrantedAuthority> getUserRole(String token) {
        Claims claims = Jwts.parser().setSigningKey(APPSECRET_KEY).parseClaimsJws(token).getBody();
        List roles = (List) claims.get(ROLE_CLAIMS);
        String json = JSONArray.toJSONString(roles);
        List<SimpleGrantedAuthority>
                grantedAuthorityList =
                JSONArray.parseArray(json, SimpleGrantedAuthority.class);
        return grantedAuthorityList;
    }
    /**
     * 是否過期
     *
     * @param token
     * @return
     */
    public static boolean isExpiration(String token) {
        Claims claims = Jwts.parser().setSigningKey(APPSECRET_KEY).parseClaimsJws(token).getBody();
        return claims.getExpiration().before(new Date());
    }
}

修改SecurityConfig配置類

@Override
    protected void configure(HttpSecurity http) throws Exception {
        List<PermissionEntity> allPermission = permissionMapper.findAllPermission();
        ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry
                expressionInterceptUrlRegistry = http.authorizeRequests();
        allPermission.forEach((permission) -> {
            expressionInterceptUrlRegistry.antMatchers(permission.getUrl()).
                    hasAnyAuthority(permission.getPermTag());
        });
        // 配置前后令牌登陸
        expressionInterceptUrlRegistry.antMatchers("/auth/login").permitAll()
                .antMatchers("/**").fullyAuthenticated()
                .and()
                //配置過濾器
                .addFilter(new JWTValidationFilter(authenticationManager()))
                .addFilter(new JWTLoginFilter(authenticationManager())).csrf().disable()
                //提出session
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    }

測試:

1、post方式請求登錄接口:localhost:8080/auth/login

請求參數(shù)json,返回請求頭中帶有token

{
    "username":"kaico_add",
    "password":"kaico"
}

2、再次請求其他接口時(shí),請求頭上帶上token,

存在的問題

Jwt如何實(shí)現(xiàn)注銷?

  • 客戶端清除緩存,比如:瀏覽器cookie清除(但是服務(wù)器還是存在)
  • 權(quán)限發(fā)生變化的情況下,管理員同志用戶重新登錄或者提示權(quán)限不足,請聯(lián)系管理員開放權(quán)限。
  • 建議將時(shí)間設(shè)置稍微短一點(diǎn)
  • 使用黑名單過濾,后期對服務(wù)器端壓力大

JWT是否安全?

安全機(jī)制肯定有

就算黑客篡改payload中的權(quán)限列表,必須先獲取到服務(wù)器端秘鑰,自己生產(chǎn)JWT才行。

JWT中存放userid

可以單獨(dú)對userid做對稱加密之后再存在payload中,解密的秘鑰在服務(wù)器端,也是安全的。

以上就是SpringSecurity詳解整合JWT實(shí)現(xiàn)全過程的詳細(xì)內(nèi)容,更多關(guān)于SpringSecurity JWT的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • SpringBoot +Vue開發(fā)考試系統(tǒng)的教程

    SpringBoot +Vue開發(fā)考試系統(tǒng)的教程

    這篇文章主要介紹了SpringBoot +Vue開發(fā)考試系統(tǒng),支持多種題型:選擇題、多選題、判斷題、填空題、綜合題以及數(shù)學(xué)公式。支持在線考試,教師在線批改試卷。本文通過實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2020-05-05
  • SpringCloud Hystrix熔斷器使用方法介紹

    SpringCloud Hystrix熔斷器使用方法介紹

    通過hystrix可以解決雪崩效應(yīng)問題,它提供了資源隔離、降級機(jī)制、融斷、緩存等功能。接下來通過本文給大家分享SpringCloud集成Hystrix熔斷,感興趣的朋友一起看看吧
    2023-03-03
  • SpringMVC詳解如何映射請求數(shù)據(jù)

    SpringMVC詳解如何映射請求數(shù)據(jù)

    這篇文章主要給大家介紹了關(guān)于SpringMvc映射請求數(shù)據(jù)的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2022-06-06
  • 一文深入理解Java中的java.lang.reflect.InvocationTargetException錯(cuò)誤

    一文深入理解Java中的java.lang.reflect.InvocationTargetException錯(cuò)誤

    這篇文章主要給大家介紹了關(guān)于Java中java.lang.reflect.InvocationTargetException錯(cuò)誤的相關(guān)資料,java.lang.reflect.InvocationTargetException是Java中的一個(gè)異常類,它通常是由反射調(diào)用方法時(shí)拋出的異常,需要的朋友可以參考下
    2024-03-03
  • java實(shí)現(xiàn)幸運(yùn)抽獎(jiǎng)功能

    java實(shí)現(xiàn)幸運(yùn)抽獎(jiǎng)功能

    這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)幸運(yùn)抽獎(jiǎng)功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-03-03
  • idea快速找到項(xiàng)目中對應(yīng)的類圖文詳解(包括源碼)

    idea快速找到項(xiàng)目中對應(yīng)的類圖文詳解(包括源碼)

    用IDEA開發(fā)Java項(xiàng)目時(shí)經(jīng)常會(huì)使用到各種快捷鍵,其中搜索是最常用的之一,下面這篇文章主要給大家介紹了關(guān)于idea如何快速找到項(xiàng)目中對應(yīng)的類(包括源碼)的相關(guān)資料,文中通過圖文介紹的非常詳細(xì),需要的朋友可以參考下
    2023-06-06
  • 在SpringBoot項(xiàng)目中解決依賴沖突問題的方法

    在SpringBoot項(xiàng)目中解決依賴沖突問題的方法

    在SpringBoot項(xiàng)目中,依賴沖突是一個(gè)常見的問題,特別是當(dāng)項(xiàng)目引入多個(gè)第三方庫或框架時(shí),依賴沖突可能導(dǎo)致編譯錯(cuò)誤、運(yùn)行時(shí)異常或不可預(yù)測的行為,本文給大家介紹了如何在SpringBoot項(xiàng)目中解決以來沖突問題的方法,需要的朋友可以參考下
    2024-01-01
  • mybatis-plus如何修改日志只打印SQL語句不打印查詢結(jié)果

    mybatis-plus如何修改日志只打印SQL語句不打印查詢結(jié)果

    這篇文章主要介紹了mybatis-plus如何修改日志只打印SQL語句不打印查詢結(jié)果問題,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-06-06
  • Java中compareTo方法使用小結(jié)

    Java中compareTo方法使用小結(jié)

    compareTo是Java中Object類中的一個(gè)方法,它的作用是比較兩個(gè)對象的大小關(guān)系,本文主要介紹了Java中compareTo方法使用小結(jié),感興趣的可以了解一下
    2024-01-01
  • springboot?@Validated的概念及示例實(shí)戰(zhàn)

    springboot?@Validated的概念及示例實(shí)戰(zhàn)

    這篇文章主要介紹了springboot?@Validated的概念以及實(shí)戰(zhàn),使用?@Validated?注解,Spring?Boot?應(yīng)用可以有效地實(shí)現(xiàn)輸入驗(yàn)證,提高數(shù)據(jù)的準(zhǔn)確性和應(yīng)用的安全性,本文結(jié)合實(shí)例給大家講解的非常詳細(xì),需要的朋友可以參考下
    2024-04-04

最新評論