Springboot整合Jwt实现用户认证

前言

  相信大家在进行用户认证中或多或少都要对用户进行认证,当前进行认证的方式有基于session、token等主流方式,但是目前使用最广泛的还是基于JWT的用户认证,特别适用于前后端分离的项目。

本篇博客我将简要的讲解JWT(Json web token)的只要知识点并实现简单的认证,如果我哪些方面有问题,请大家激烈评论,我好进一步修改!

1.Jwt的组成

  将Jwt是由头部Header、载荷payload、以及签名sign三个部分组成

  1.1 Herder

     头部包含签名的生成token的'typ'以及'alo'也就是签名的类型和哪一种算法,还有签名中部分的编码格式

  1.1 payload

        载荷中存储部分用户的信息,但是最好不要将密码存储在载荷中,因为头部以及载荷安全性不高

  1.1 sign

     JWT的安全性体现在签名中,它是由头部、载荷以及盐进行Base64加密而成的,中间使用“.”连接,最后使用头部的编码格式编码得到签名。因为头部和载荷的安全性较低,所以我们主要通过盐来防止token伪造,一般通过

     HMAC256算法对其进行加密

2.Jwt的认证流程

 

  上述图详细地叙述了通过JWT进行认证的过程

  1)用户第一次在浏览器网页里面输入用户名和密码进行认证,当客户端收到用户信息后对用户信息进行验证

  2)客户端对用户信息验证无误后,返回一个token给浏览器

  3)用户下次登录的时候携带这个token进行登录

  4)客户端验证token无误后就放行

  5)服务端返回用户访问客户端资源

3.Jwt的实现

  3.1 生成token

private static final long EXPIRE_TIME = 1800L;//单位为秒
    private static final String TOKEN_SECRET = "woToken";

    /**
     * 生成token
     * @param sysUser
     * @return
     */
    public static String sign(SysUser sysUser){
        String token = null;

        try {
            Date expireDate = new Date(System.currentTimeMillis() + EXPIRE_TIME * 1000);
            Map<String, Object> map = new HashMap<>();
            map.put("alg", "HS256");
            map.put("typ", "JWT");
            token = JWT.create()
                    .withHeader(map)//添加头部
                    .withIssuer("auth0")
                    .withClaim("uid", sysUser.getUid())//载体中设置uid
                    .withClaim("username", sysUser.getUsername())//载体中设置username
                    .withExpiresAt(expireDate)//设置过期时间
                    .withIssuedAt(new Date())//设置签发时间
                    .sign(Algorithm.HMAC256(TOKEN_SECRET));//secret加密
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (JWTCreationException e) {
            e.printStackTrace();
        }
        return token;
    }
生成token

  3.2 验证token

/**
     * 校验token并解析token
     */
    public static Boolean verifyToken(String token){
        try {
            JWTVerifier verifier = JWT.require(Algorithm.HMAC256(TOKEN_SECRET)).withIssuer("auth0").build();
            DecodedJWT decodedJWT = verifier.verify(token);
            return true;
        } catch (JWTVerificationException e) {
            e.printStackTrace();
            return false;
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
            return false;
        }
    }
验证token

  3.3 获取token中的uid

/**
     * 获取token中的payload信息
     * @param token
     * @return
     */
    public static Integer getUid(String token){
        try {
            DecodedJWT jwt = JWT.decode(token);
            return jwt.getClaim("uid").asInt();
        } catch (JWTDecodeException e) {
            return null;
        }
    }
获取载荷信息
package com.ku.wo.utils;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTCreationException;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.ku.wo.entity.SysUser;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * 生成Jwt和认证
 */
public class JwtUtils {

    private static final long EXPIRE_TIME = 1800L;//单位为秒
    private static final String TOKEN_SECRET = "woToken";

    /**
     * 生成token
     * @param sysUser
     * @return
     */
    public static String sign(SysUser sysUser){
        String token = null;

        try {
            Date expireDate = new Date(System.currentTimeMillis() + EXPIRE_TIME * 1000);
            Map<String, Object> map = new HashMap<>();
            map.put("alg", "HS256");
            map.put("typ", "JWT");
            token = JWT.create()
                    .withHeader(map)//添加头部
                    .withIssuer("auth0")
                    .withClaim("uid", sysUser.getUid())//载体中设置uid
                    .withClaim("username", sysUser.getUsername())//载体中设置username
                    .withExpiresAt(expireDate)//设置过期时间
                    .withIssuedAt(new Date())//设置签发时间
                    .sign(Algorithm.HMAC256(TOKEN_SECRET));//secret加密
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (JWTCreationException e) {
            e.printStackTrace();
        }
        return token;
    }

    /**
     * 校验token并解析token
     */
    public static Boolean verifyToken(String token){
        try {
            JWTVerifier verifier = JWT.require(Algorithm.HMAC256(TOKEN_SECRET)).withIssuer("auth0").build();
            DecodedJWT decodedJWT = verifier.verify(token);
            return true;
        } catch (JWTVerificationException e) {
            e.printStackTrace();
            return false;
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 获取token中的payload信息
     * @param token
     * @return
     */
    public static Integer getUid(String token){
        try {
            DecodedJWT jwt = JWT.decode(token);
            return jwt.getClaim("uid").asInt();
        } catch (JWTDecodeException e) {
            return null;
        }
    }

}
JWT生成、校验、获取载荷信息全部代码

  3.4 拦截请求

package com.ku.wo.interceptor;

import com.ku.wo.utils.JwtUtils;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 用于获取token并校验
 */

/**
 * 单体项目验证token使用拦截器,分布式项目使用网关
 */
@Component
public class LoginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//        //不使用JWT使用的登录
////        if (request.getSession().getAttribute("uid") == null){
////            return false;//如果这里为false,后面的都不执行,并且postHandle()方法也不执行
////        }
////        return true;

        if(HttpMethod.OPTIONS.toString().equals(request.getMethod())){
            System.out.println("OPTIONS请求,放行!");
            return true;
        }
        
        String token = request.getSession().getAttribute("token").toString();
        System.out.println("拦截器的token获取"+token);
//        String token = request.getHeader("token");
        if(JwtUtils.verifyToken(token)){
            return true;
        }
        //失败时返回失败信息
        return false;
    }
}
拦截请求

  3.5 拦截资源

package com.ku.wo.config;

import com.ku.wo.interceptor.LoginInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.ArrayList;
import java.util.List;

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Autowired(required = false)
    LoginInterceptor loginInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //创建拦截器对象

        //白名单
        List<String> patterns = new ArrayList<>();
        patterns.add("/user/reg");
        patterns.add("/user/login");
        registry.addInterceptor(loginInterceptor)
                .addPathPatterns("/**")//默认对所有请求进行拦截
                .excludePathPatterns(patterns);
    }


}
拦截资源

  3.6 controller执行

@PostMapping("/login")//在浏览器上面应该改为get
    public JsonResult<String> login(@RequestParam(value = "name", required = false) String username,
                                         @RequestParam(value = "pwd", required = false) String password, HttpSession session){
        SysUser data = userDetailsService.login(username, password);
        String token = JwtUtils.sign(data);
        session.setAttribute("token", token);
        session.setAttribute("user", data);
        session.setAttribute("uid",data.getUid());
        session.setAttribute("username", username);
        return new JsonResult<String>(OK, token);
    }

    @PostMapping("/get_login_data")//在浏览器上面应该改为get
    public JsonResult<SysUser> getLoginData(String token, HttpSession session){
            if(!session.getAttribute("token").equals(token)){
                return new JsonResult<>(ERROR);
            }
        SysUser data = (SysUser)session.getAttribute("user");
        return new JsonResult<SysUser>(OK, data);
    }

//JSONResult响应类
package com.ku.wo.utils;

import lombok.Data;

import java.io.Serializable;

@Data
//原文使用的是<E>因为属性data属于元素,但是我在网上找到使用T貌似更好,
// 尝试一下使用T的情况怎么样,List<T>表示集合中元素都为T类型,网上使用的大多数也是T
public class JsonResult<T> implements Serializable {
    private Integer state;
    private String message;
    private T data;
    //必须要有无参构造
    public JsonResult(){
        super();
    }
    public JsonResult(Integer state){
        super();
        this.state = state;
    }

    public JsonResult(Throwable e){//Throwable是error和Exception的父类,里面有getMessage()方法获取错误和异常信息
        super();
        this.message = e.getMessage();
    }

    public JsonResult(Integer state, T data){
        super();
        this.state = state;
        this.data = data;
    }
}
controller测试方法

4.Jwt测试

5.参考链接

https://blog.csdn.net/m0_56966142/article/details/123398543?ops_request_misc=&request_id=&biz_id=102&utm_term=%E4%BD%BF%E7%94%A8JWT%E5%AE%9E%E7%8E%B0%E7%94%A8%E6%88%B7%E8%AE%A4%E8%AF%81&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-0-123398543.142^v63^control_1,201^v3^control_2,213^v2^t3_esquery_v2&spm=1018.2226.3001.4187

 

posted @ 2022-11-16 10:31  求知律己  阅读(172)  评论(0编辑  收藏  举报