Home  >  Article  >  Java  >  How does springboot integrate JWT to achieve identity authentication?

How does springboot integrate JWT to achieve identity authentication?

王林
王林forward
2023-05-14 19:25:101479browse

1. What is JWT

JSON Web Token (JWT) is currently the most popular cross-domain authentication solution. Current project development generally involves separation of the front-end and the end-end, which involves cross-domain and authentication issues.

2. JWT composition

It consists of three parts: Header, Payload and signature

Header :

The header information consists of two parts: 1. The type of token, that is, JWT; 2. The signature algorithm used, such as HMASSHA256 or RSA;

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

In this json The typ attribute is used to identify that the entire token string is a JWT string; its alg attribute is used to describe the signature and digest algorithm used when issuing this JWT. The full names of the typ and alg attributes are actually type algorithm, respectively. The meaning of type and algorithm. The reason why they are represented by three letters is also based on the consideration of the final string size of JWT. It is also consistent with the name of JWT, so they are all three characters...typ and alg are the attributes specified in the JWT standard. name.

Payload:

payload is used to carry the data to be transmitted. Its json structure is actually a set of declarations for the data to be transmitted by JWT. These statements are called claims by the JWT standard. One of its "attribute value pairs" is actually a claim (requirement). Each claim represents a specific meaning and function.

We can put some business information in the claim.

Signature:

The signature is the two strings obtained after base64url encoding the json structures corresponding to the header and payload and splicing them together with 'English period' , and then generated according to the signature algorithm specified by alg in the header.
Different algorithms have different signature results. Take alg: HS256 as an example to illustrate how to obtain the previous signature.

According to the previous description of the available values ​​of alg, HS256 actually contains two algorithms: HMAC algorithm and SHA256 algorithm. The former is used to generate a digest, and the latter is used to digitally sign the digest. These two algorithms can also be collectively referred to as HMACSHA256

jwt data structure diagram:

How does springboot integrate JWT to achieve identity authentication?

3. JWT operating principle

1. Once a login request is sent, it will inevitably carry user information uname and pwd

2. If the user information uname and pwd are successfully logged in, the user information will be generated into an encrypted string through the jwt tool class

3. The encrypted string will be sent to the front end in the form of a response header

4. The front-end server will have a response interceptor to intercept the jwt string carried by the response header and put it into Vuex

5. When requesting for the second time, there is a request interceptor in the front-end server, which will put the jwt string in Vuex into the request header request

6. When the request passes through a cross-domain method Reaching the backend server, there is another filter in the backend server, which will intercept the jwt string in the request header

7. The jwt tool class will parse the jwt string, parse it into user information, and finally verify it.

4. Springboot integrates JWT

Overall idea:

When the front end accesses the background login interface to log in, it first determines whether the user exists in the user table based on the user name and password. If it exists For this user, a jwt string is generated. You can add some business information to the jwt string (such as login account, user's real name, etc.), and return the jwt string to the front end.

The current end gets the jwt string and puts it in to the header of all requests, such as token=jwt string

Develop a filter on the backend to intercept all requests (except login requests, because the login request has not yet generated jwt), and obtain the jwt from the header of the request (That is, the value of the token), check the jwt and obtain the business information in the jwt, and put the business information in the header of the request, so that the back-end interface can obtain it directly from the header

If the filter If the jwt expires or the verification fails, a prompt will be returned to the front end, and the front end will return to the login page to allow the user to log in again.

1. Introduce dependencies in pom.xml

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

2. Develop jwt generation tool class, the code is as follows:

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";
 
 
    /**
     * 创建携带自定义信息和声明的自定义私钥的jwt
     * @param user  用户信息表
     * @return  jwt串
     */
    public static String creatJwt(TabUser user){
        //构建头部信息
        Map<String,Object> header = new HashMap<>();
        header.put("typ","JWT");
        header.put("alg","HS256");
 
        //根据私钥构建密钥信息
        Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET);
 
        //根据当前用户密码构建密钥信息
//        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())//业务信息:员工号
                .withClaim("username",user.getUsername())//业务信息:员工姓名
                .withClaim("rolename",user.getRoleName())//业务信息:角色
                .withIssuer("SERVICE")//声明,签名是有谁生成 例如 服务器
                .withNotBefore(new Date())//声明,定义在什么时间之前,该jwt都是不可用的
                .withExpiresAt(expireDate)//声明, 签名过期的时间
                .sign(algorithm);//根据algorithm生成签名
 
        return jwt;
 
    }
}

3. The back-end login interface logic is as follows:

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));
 
        //生成带有业务信息的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. Develop filter and perform jwt verification

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自行校验,如果超时了,会执行catch里代码
                DecodedJWT decodeJwt = JWT.require(Algorithm.HMAC256("aa082c-66rt89-29sr3t-y9t7b8")).build().verify(token);
                
                //获取jwt中的业务信息
                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);
                
                //把业务信息添加到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中放业务信息
     * @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实现类="+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实现类="+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() {
 
    }
}

The above is the detailed content of How does springboot integrate JWT to achieve identity authentication?. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:yisu.com. If there is any infringement, please contact admin@php.cn delete