SpringBoot集成JWT实现token验证

有关session存储用户信息在spring系列springsession文章中有写,session缺点是占用服务器资源,配置多台服务器后又需要对session进行统一存储(redis),保证每台服务器都可以取到正确的session。
JWT不用缓存数据库redis来实现用户信息的共享,也可以达到一次登录,处处可见
JWT全称JSON Web Token,实现过程简单的说就是用户登录成功之后,将用户的信息进行加密,然后生成一个token返回给客户端,与传统的session交互没太大区别。省掉了redis,把用户信息存到token中,这样客户端、服务端都可以从token中获取用户的基本信息,既然客户端可以获取,肯定是不能存放敏感信息的,因为浏览器可以直接从token获取用户信息。
交互流程:
SpringBoot集成JWT实现token验证_第1张图片

1.JWT具体内容

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6MSwiZXhwIjoxNjU0MjIzMzAwLCJpYXQiOjE2NTQyMjMyNzAsInVzZXJuYW1lIjoiemhhbmdzYW4ifQ.pO0gpz6AuDFtBAEOlTV09-BJVIIxqSGP-k_fcDrVhdw

1.header(头部)

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
使用base64加密,用于存放token类型和加密协议.
SpringBoot集成JWT实现token验证_第2张图片

2.payload(载荷)

eyJpZCI6MSwiZXhwIjoxNjU0MjIzMzAwLCJpYXQiOjE2NTQyMjMyNzAsInVzZXJuYW1lIjoiemhhbmdzYW4ifQ
存放有效信息
SpringBoot集成JWT实现token验证_第3张图片

3.signature

pO0gpz6AuDFtBAEOlTV09-BJVIIxqSGP-k_fcDrVhdw
这个部分需要base64加密后的header和base64加密后的payload使用.连接组成的字符串,然后通过header中声明的加密方式进行加盐secret组合加密,然后就构成了jwt的第三部分。

2.springboot整合

1.pom

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

2.用户实体类

@Data
public class User {
    private Long id;
    /**
     * 用户名
     */
    private String username;
    /**
     * 密码
     */
    private String password;
}

3.TokenUtil工具类

用于创建、获取、验证Token

@Slf4j
@Component
public class TokenUtil {

    //密钥
    public static final String SECRET = "youareapig??shabixiangpojie?";
    //过期时间:秒
    public static final int EXPIRE = 30;
    /**
     * 生成Token
     */
    public static String createToken(User user){
        Calendar nowTime = Calendar.getInstance();
        //过期时间
        nowTime.add(Calendar.SECOND, EXPIRE);
        Date expireDate = nowTime.getTime();
        String token = JWT.create()
        		//这是在设置第二部分信息,不要设置密码之类的,因为这些信息可以通过浏览器获取
        		//用户id
                .withClaim("id", user.getId())
                //用户名
                .withClaim("username",user.getUsername())
                //创建token的时间
                .withIssuedAt(new Date())//签名时间
                //设置token的过期时间
                .withExpiresAt(expireDate)//过期时间
                //设置第一部分
                .sign(Algorithm.HMAC256(SECRET));//签名
        return token;
    }

    /**
     * 验证token
     */
    public static DecodedJWT verify(String token) {
        //如果有任何验证异常,此处都会抛出异常 我们需要在拦截器调用这个方法,捕获异常,然后返回错误信息给前端
        DecodedJWT decodedJWT = JWT.require(Algorithm.HMAC256(SECRET)).build().verify(token);
        return decodedJWT;
    }

    /**
     * 获取token中的 payload 也就是第二部分的信息
     */
    public static DecodedJWT getTokenInfo(String token) {
        DecodedJWT decodedJWT = JWT.require(Algorithm.HMAC256(SECRET)).build().verify(token);
        //使用 TokenUtils.getTokenInfo(token).getClaim("account").asString()
        return decodedJWT;
    }
}

4.拦截器

@Component
public class AuthenticationInterceptor implements HandlerInterceptor {

    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws Exception {
        System.out.println("进入拦截器");
        //实际这个名字可以指定为别的,token太没有辨识度---
        //这个header是在创建完token返回给前端时指定的头部的key,vakue就是token内容
        String token=httpServletRequest.getHeader("token");
        Map<String, Object> map = new HashMap<>();
        try {
        	//这里尽行token验证,捕获异常,正常的话也不需要处理,直接抛出异常,由统一异常处理类进行处理,然后返回给前端统一数据类型。
            TokenUtil.verify(token);
            return true;
        } catch (SignatureVerificationException e) {
            e.printStackTrace();
            map.put("msg", "签名不一致");
            map.put("code",500);
        } catch (TokenExpiredException e) {
            e.printStackTrace();
            map.put("msg", "令牌过期");
            map.put("code",500);
        } catch (AlgorithmMismatchException e) {
            e.printStackTrace();
            map.put("msg", "算法不匹配");
            map.put("code",500);
        } catch (InvalidClaimException e) {
            e.printStackTrace();
            map.put("msg", "失效的payload");
            map.put("code",500);
        } catch (Exception e) {
            e.printStackTrace();
            map.put("msg", "token无效");
            map.put("code",500);
        }
        //根据自己所需选择所需的异常处理
        map.put("state", false);
        //响应到前台: 将map转为json
        String json = new ObjectMapper().writeValueAsString(map);
        httpServletResponse.setContentType("application/json;charset=UTF-8");
        httpServletResponse.getWriter().println(json);
        return false;
    }
}

5.注册拦截器

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        List<String> excludePathLists= new ArrayList<>();
        //注册、登录允许访问,不进行拦截
        excludePathLists.add("/user/login");
        excludePathLists.add("/user/register");
        excludePathLists.add("/user/info");
		
        registry.addInterceptor(new AuthenticationInterceptor()).addPathPatterns("/**").excludePathPatterns(excludePathLists);
    }
}

参考链接

6.创建controller进行测试

@RestController
public class UserController {
	//模拟登录
    @PostMapping("/user/login")
    public String LoginUser(HttpServletResponse servletResponse){
    	//这里正常前端会传进来用户信息,然后去数据库查询,能查到,并且账号密码匹配成功,则可以登录。
    	//这里只是模拟,随便创建了一个用户信息
        User user= new User();
        user.setUsername("zhangsan");
        user.setPassword("123456");
        user.setId(1L);
        //根据用户信息创建token,(登录成功的处理逻辑)
        String myToken = TokenUtil.createToken(user);
        //设置header
        servletResponse.setHeader("token",myToken);
        //获取token,获取过期时间进行返回
        DecodedJWT tokenInfo = TokenUtil.getTokenInfo(myToken);
        Date expiresAt = tokenInfo.getExpiresAt();
        return expiresAt.toString();
    }
    //测试其他账号登录
    @PostMapping("/zhangsan")
    public String testLogin(){
        return "ok";
    }
}

7.postman测试

1.最一开始还没登录过,肯定访问不到。
SpringBoot集成JWT实现token验证_第4张图片
2.模拟登录成功,返回了过期时间
SpringBoot集成JWT实现token验证_第5张图片
携带的header
SpringBoot集成JWT实现token验证_第6张图片
3.再测试其他用户登录
携带token请求头
SpringBoot集成JWT实现token验证_第7张图片
测试过期
SpringBoot集成JWT实现token验证_第8张图片

至此一个简单的token登录机制就实现了~

你可能感兴趣的:(springboot,spring,boot,java,spring)