溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

springboot怎么集成JWT實現(xiàn)身份認證

發(fā)布時間:2023-05-08 14:27:09 來源:億速云 閱讀:68 作者:zzz 欄目:開發(fā)技術

今天小編給大家分享一下springboot怎么集成JWT實現(xiàn)身份認證的相關知識點,內(nèi)容詳細,邏輯清晰,相信大部分人都還太了解這方面的知識,所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來了解一下吧。

一、什么是JWT

JSON Web Token (JWT),它是目前最流行的跨域身份驗證解決方案。現(xiàn)在的項目開發(fā)一般都是前端端分離,這就涉及到跨域和權鑒問題。

二、JWT組成

由三部分組成:頭部(Header)、載荷(Payload)與簽名(signature)

頭部(Header):

頭部信息由兩部分組成1、令牌的類型,即JWT;2、使用的簽名算法,例如HMASSHA256或RSA;      

{
  "alg": "HS256",
  "typ": "JWT"
}

這個json中的typ屬性,用來標識整個token字符串是一個JWT字符串;它的alg屬性,用來說明這個JWT簽發(fā)的時候所使用的簽名和摘要算法,typ跟alg屬性的全稱其實是type algorithm,分別是類型跟算法的意思。之所以都用三個字母來表示,也是基于JWT最終字串大小的考慮,同時也是跟JWT這個名稱保持一致,這樣就都是三個字符了…typ跟alg是JWT中標準中規(guī)定的屬性名稱。

載荷(Payload):

payload用來承載要傳遞的數(shù)據(jù),它的json結(jié)構(gòu)實際上是對JWT要傳遞的數(shù)據(jù)的一組聲明,這些聲明被JWT標準稱為claims,它的一個“屬性值對”其實就是一個claim(要求), 每一個claim的都代表特定的含義和作用。

我們可以在claim里放一些業(yè)務信息。

簽名(signature):

簽名是把header和payload對應的json結(jié)構(gòu)進行base64url編碼之后得到的兩個串用 '英文句點號' 拼接起來,然后根據(jù)header里面alg指定的簽名算法生成出來的。
算法不同,簽名結(jié)果不同。以alg: HS256為例來說明前面的簽名如何來得到。

按照前面alg可用值的說明,HS256其實包含的是兩種算法:HMAC算法和SHA256算法,前者用于生成摘要,后者用于對摘要進行數(shù)字簽名。這兩個算法也可以用HMACSHA256來統(tǒng)稱    

jwt數(shù)據(jù)結(jié)構(gòu)圖:

springboot怎么集成JWT實現(xiàn)身份認證

三、JWT運行原理

1.第一次發(fā)送登錄請求,必然會攜帶用戶信息uname和pwd

2.通過用戶信息uname和pwd登錄成功,會將用戶信息通過jwt工具類生成一個加密的字符串

3.加密字符串 會以response header 響應頭的形式 相應到前端

4.前端服務器會有響應攔截器攔截,截取到響應頭承載的jwt串,又會放到Vuex中

5.當?shù)诙握埱?,前端服務器中有一個請求攔截器,會將Vuex中的jwt串放入request header 請求當中

6.當請求通過跨域的方式到達后臺服務器,后臺服務器中又有一個過濾器,會截取到 request header 請求當中的jwt串

7.jwt工具類會對jwt串進行解析,解析成用戶信息,最終進行校驗

四、springboot集成JWT

整體思路:

當前端訪問后臺登錄接口login時,先根據(jù)用戶名和密碼判斷用戶表是否存在該用戶,如果存在該用戶,則生成jwt串,可以在jwt串里添加些業(yè)務信息(比如用登錄賬號,用戶真實姓名等),并把jwt串返給前端

當前端拿到jwt串后放到所有請求的header中,比如token=jwt串

后端開發(fā)一個filter,攔截所有請求(除login請求外,因為login請求還沒有生成jwt),并從request的header中獲取jwt(即token的值),對jwt校驗和獲取jwt中的業(yè)務信息,在把這些業(yè)務信息放到request的header中,這樣方便后端接口直接從header中獲取

如果filter中jwt過期,或者校驗失敗,則返回給前端提示,前端返回登錄頁面,讓用戶重新登錄。

1、在pom.xml引入依賴    

<!--jwt-->
        <dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>3.8.3</version>
        </dependency>

2、開發(fā)jwt生成工具類,代碼如下:

package com.lsl.exam.utils;
 
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.lsl.exam.entity.TabUser;
 
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
 
public class JwtUtil {
 
    private static final long EXPIRE_TIME = 1000 * 60 * 60 *24;
 
    //設置私鑰
    private static final String TOKEN_SECRET = "aa082c-66rt89-29sr3t-y9t7b8";
 
 
    /**
     * 創(chuàng)建攜帶自定義信息和聲明的自定義私鑰的jwt
     * @param user  用戶信息表
     * @return  jwt串
     */
    public static String creatJwt(TabUser user){
        //構(gòu)建頭部信息
        Map<String,Object> header = new HashMap<>();
        header.put("typ","JWT");
        header.put("alg","HS256");
 
        //根據(jù)私鑰構(gòu)建密鑰信息
        Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET);
 
        //根據(jù)當前用戶密碼構(gòu)建密鑰信息
//        Algorithm algorithm = Algorithm.HMAC256(user.getUserpwd());
 
        //設置過期時間為當前時間一天后
        Date nowDate = new Date();
        Date expireDate = new Date(System.currentTimeMillis() + EXPIRE_TIME);
 
        String jwt = JWT.create().withHeader(header)
                .withClaim("account",user.getAccount())//業(yè)務信息:員工號
                .withClaim("username",user.getUsername())//業(yè)務信息:員工姓名
                .withClaim("rolename",user.getRoleName())//業(yè)務信息:角色
                .withIssuer("SERVICE")//聲明,簽名是有誰生成 例如 服務器
                .withNotBefore(new Date())//聲明,定義在什么時間之前,該jwt都是不可用的
                .withExpiresAt(expireDate)//聲明, 簽名過期的時間
                .sign(algorithm);//根據(jù)algorithm生成簽名
 
        return jwt;
 
    }
}

3、后端login接口邏輯如下:

package com.lsl.exam.controller;
 
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.lsl.exam.entity.TabUser;
import com.lsl.exam.entity.backresult.ResultVO;
import com.lsl.exam.service.ITabRoleService;
import com.lsl.exam.service.IUserService;
import com.lsl.exam.utils.Base64Util;
import com.lsl.exam.utils.JwtUtil;
import com.lsl.exam.utils.ResultVoUtil;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
 
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
 
@RestController
@RequestMapping("/exam")
public class UserController {
    private static final Logger LOG = org.slf4j.LoggerFactory.getLogger("UserController");
 
    @Autowired
    IUserService userService;
 
    @Autowired
    ITabRoleService roleService;
 
    @PostMapping(value = "login",produces = "application/json;charset=UTF-8")
    @ResponseBody
    public ResultVO<?> login(@RequestBody Map params){
        Map reuslt = new HashMap();
 
        String account = params.get("account") == null ? "" : params.get("account").toString();
        String pwd = params.get("pwd") == null ? "" : params.get("pwd").toString();
 
        if ("".equals(account) || "".equals(pwd)){
            return ResultVoUtil.error(30000,"用戶名或者密碼不能為空!");
        }
 
        //pwd解密
        String decodePwd = Base64Util.decode(pwd);
        if ("".contains(decodePwd)){
            return ResultVoUtil.error(30000,"密碼錯誤!");
        }
 
        TabUser user = userService.getOne(new QueryWrapper<TabUser>()
                .eq("account",account)
                .eq("userpwd",decodePwd));
        if (null == user){
            return ResultVoUtil.error(30000,"用戶名或者密碼錯誤");
        }
        
        //獲取當前用戶擁有的角色
        String userId = user.getId();
        Map roleMap = new HashMap();
        roleMap.put("userId",userId);
        List<Map> roleList = roleService.qryRoleInfoByUserId(roleMap);
        List<String> roleNames = new ArrayList<>();
        for(Map role : roleList){
            roleNames.add(role.get("role").toString());
        }
        user.setRoleName(JSON.toJSONString(roleNames));
 
        //生成帶有業(yè)務信息的jwt串
        String jwt = JwtUtil.creatJwt(user);
        
        //把jwt和當前用戶信息返給前端
        reuslt.put("jwt",jwt);
        reuslt.put("roleNames",roleNames);
        reuslt.put("username",user.getUsername());
        reuslt.put("account",user.getAccount());
        
        return ResultVoUtil.success(reuslt);
    }
 
    @PostMapping(value = "qryUser",produces = "application/json;charset=UTF-8")
    @ResponseBody
    public Object qryUser(HttpServletRequest request){
 
        //這里header中的信息是filter中放進去的
        String account = request.getHeader("account");
        String username = request.getHeader("username");
        String rolename = request.getHeader("rolename");
 
        List<TabUser> list = userService.list();
        return ResultVoUtil.success(list);
    }
}

4、開發(fā)filter,進行jwt校驗

package com.lsl.exam.filter;
 
import com.alibaba.fastjson.JSON;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.lsl.exam.entity.backresult.ResultVO;
import com.lsl.exam.utils.ResultVoUtil;
import org.apache.tomcat.util.http.MimeHeaders;
import org.springframework.stereotype.Component;
 
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
 
/**
 * jwt校驗過濾器
 */
@Component
@WebFilter(filterName = "jwtFilter",urlPatterns = {"/*"})
public class AuthJwtFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
 
    }
 
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
        String url = httpServletRequest.getRequestURL().toString();
        
        //配置不進行jwt校驗的請求路徑
        List<String> urlList = new ArrayList<>();
        urlList.add("/exam/login");
 
        boolean flag = false;
        for (String strUrl : urlList){
            if (url.contains(strUrl)){
                flag = true;
            }
        }
 
        try {
            if (!flag){
                String token = httpServletRequest.getHeader("token");
                //校驗token,jwt過期有jwt自行校驗,如果超時了,會執(zhí)行catch里代碼
                DecodedJWT decodeJwt = JWT.require(Algorithm.HMAC256("aa082c-66rt89-29sr3t-y9t7b8")).build().verify(token);
                
                //獲取jwt中的業(yè)務信息
                String account = decodeJwt.getClaim("account").asString();
                String username = decodeJwt.getClaim("username").asString();
                String rolename = decodeJwt.getClaim("rolename").asString();
                Map<String, String> headerMap = new HashMap<>();
                headerMap.put("account",account);
                headerMap.put("username",username);
                headerMap.put("rolename",rolename);
                
                //把業(yè)務信息添加到request的header
                addHeader(httpServletRequest,headerMap);
 
 
//                Class<?> superclass = servletRequest.getClass().getSuperclass().getSuperclass();
//                Field requestField = superclass.getDeclaredField("request");
//                requestField.setAccessible(true);
//                RequestFacade requestFacadeInstance = (RequestFacade) requestField.get(servletRequest);
                RequestFacade requestFacadeInstance = (RequestFacade)superclass3;
//                Field requestField1 = requestFacadeInstance.getClass().getDeclaredField("request");
//                requestField1.setAccessible(true);
//                Object requestInstance =  requestField1.get(requestFacadeInstance);
//                Field coyoteRequestField = requestInstance.getClass().getDeclaredField("coyoteRequest");
//                coyoteRequestField.setAccessible(true);
//
//                Object coyoRequestInstance =  requestField1.get(requestInstance);
//                Field headersField = coyoRequestInstance.getClass().getDeclaredField("headers");
//                headersField.setAccessible(true);
//
//                MimeHeaders headers = (MimeHeaders) headersField.get(coyoRequestInstance);
//                headers.removeHeader("token");
//                headers.addValue("account").setString(account);
//                headers.addValue("username").setString(username);
//                headers.addValue("roleid").setString(roleid);
//
 
            }
        } catch (Exception e) {
            //jwt校驗失敗,返給前端的code=1,前端要重定向到登錄頁面
            PrintWriter writer = null;
            servletResponse.setCharacterEncoding("UTF-8");
            servletResponse.setContentType("text/html; charset=utf-8");
 
            try {
                writer = servletResponse.getWriter();
                ResultVO vo = ResultVoUtil.successLogout();
                String msg = JSON.toJSONString(vo);
                writer.println(msg);
            } catch (IOException ex) {
 
            } finally {
                if (writer != null){
                    writer.close();
                }
                return;
            }
 
        }
 
        filterChain.doFilter(servletRequest,servletResponse);
    }
 
    /**
     * 向request的header中放業(yè)務信息
     * @param request
     * @param headerMap
     */
    private void addHeader(HttpServletRequest request, Map<String, String> headerMap) {
        if (headerMap==null||headerMap.isEmpty()){
            return;
        }
 
        Class<? extends HttpServletRequest> c=request.getClass();
        //System.out.println(c.getName());
        System.out.println("request實現(xiàn)類="+c.getName());
        try{
            Field requestField=c.getDeclaredField("request");
            requestField.setAccessible(true);
 
            Object o=requestField.get(request);
            Field coyoteRequest=o.getClass().getDeclaredField("coyoteRequest");
            coyoteRequest.setAccessible(true);
 
            Object o2=coyoteRequest.get(o);
            System.out.println("coyoteRequest實現(xiàn)類="+o2.getClass().getName());
            Field headers=o2.getClass().getDeclaredField("headers");
            headers.setAccessible(true);
 
            MimeHeaders mimeHeaders=(MimeHeaders) headers.get(o2);
            for (Map.Entry<String,String> entry:headerMap.entrySet()){
                mimeHeaders.removeHeader(entry.getKey());
                mimeHeaders.addValue(entry.getKey()).setString(entry.getValue());
            }
 
        }catch (Exception e){
            e.printStackTrace();
        }
 
    } 
    @Override
    public void destroy() {
 
    }
}

以上就是“springboot怎么集成JWT實現(xiàn)身份認證”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家閱讀完這篇文章都有很大的收獲,小編每天都會為大家更新不同的知識,如果還想學習更多的知識,請關注億速云行業(yè)資訊頻道。

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內(nèi)容。

AI