EMOS个人教程-第3章 从零构建后端项目进阶篇

1 章节介绍

2 Shiro和JWT技术

2.1 Shiro简介

  • 什么是认证?
  • 什么是授权?
  • Shiro靠什么做认证与授权的?

2.2 JWT简介

  • 单点系统中的认证方式
  • 集群环境中的JWT认证方式

3 创建JwtUtil工具类

3.1 导入依赖库

        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-web</artifactId>
            <version>1.7.1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.7.1</version>
        </dependency>
        <dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>3.18.2</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.12.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
emos:
  jwt:
    secret: 123456
    expire: 5
    cache-expire: 10

3.2 生成令牌

  • 密钥
  • 过期时间
  • 用户ID

3.3 验证令牌的有效性

  • 内容是否有效
  • 是否过期
package com.example.emos.wx.config.shiro;

import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateUtil;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.util.Date;

@Component
@Slf4j
public class JwtUtil {
    @Value("${emos.jwt.secret}")
    private String secret;

    @Value("${emos.jwt.expire}")
    private int expire;

    public String createToken(int userId){
        Date date=DateUtil.offset(new Date(), DateField.DAY_OF_YEAR,5);
        Algorithm algorithm=Algorithm.HMAC256(secret);
        JWTCreator.Builder builder= JWT.create();
        String token=builder.withClaim("userId",userId).withExpiresAt(date).sign(algorithm);
        return token;
    }

    public int getUserId(String token){
        DecodedJWT jwt=JWT.decode(token);
        int userId=jwt.getClaim("userId").asInt();
        return userId;
    }

    public void verifierToken(String token){
        Algorithm algorithm=Algorithm.HMAC256(secret);
        JWTVerifier verifier=JWT.require(algorithm).build();
        verifier.verify(token);
    }
}

4 把令牌封装成认证对象

4.1 Shiro框架的认证需要用到认证对象

4.2 把令牌字符串做简单的封装

public class OAuth2Token implements AuthenticationToken{}

5 创建OAuth2Realm类(新)

5.1 创建AuthorizingRealm类的子类

5.2 实现认证与授权的方法

@Component
public class OAuth2Realm extends AuthorizingRealm{}

6 刷新令牌应该如何设计?

6.1 为什么要刷新令牌?

  • 令牌一旦生成就保存在客户端
  • 即便用户一直在登陆使用系统,也不会重新生成令牌
  • 令牌到期,用户必须重新登录
  • 令牌应该自动续期

6.2 双令牌机制

  • 设置长短日期的令牌
  • 短日期令牌失效,就用长日期的令牌

6.3 缓存令牌机制

  • 令牌缓存到Redis上面
  • 缓存的令牌过期时间是客户端令牌的一倍
  • 如果客户端令牌过期,缓存令牌没有过期,则生成新的令牌
  • 如果客户端令牌过期,缓存令牌也过期了,则用户必须重新登录

7 创建存储令牌的媒介类

  • 该类是用于在过滤器和AOP之间传递Token
  • 因为使用了ThreadLocal,所以是线程安全的
@Component
public class ThreadLocalToken {
    private ThreadLocal<String> local=new ThreadLocal<>();

    public void setToken(String token){
        local.set(token);
    }

    public String getToken(){
        return local.get();
    }

    public void clear(){
        local.remove();
    }
}

8 创建OAuth2Filter类(一)

8.1 判断哪些请求应该被Shiro处理

  • options请求直接放行
    • 提交application/json数据
    • 请求被分成options和post两次
  • 其余所有请求都要被Shiro处理

8.2 判断Token是真过期还是假过期

  • 真过期,返回提示信息,让用户重新登录
  • 假过期,就生成新的令牌,返回给客户端

8.3 存储新令牌

  • ThreadLocalToken
  • Redis

9 创建OAuth2Filter类(二)

10 创建ShiroConfig类

10.1 把Filter和Realm添加到Shiro框架

10.2 创建四个对象返回给SpringBoot

  • SecurityManager
    • 用于封装Realm对象
  • ShiroFilterFactoryBean
    • 用于封装Filter对象
    • 设置Filter拦截路径
  • LifecycleBeanPostProcessor
    • 管理Shiro对象生命周期
  • AuthorizationAttributeSourceAdvisor
    • AOP切面类
    • Web方法执行前,验证权限

11 利用切面类向客户端返回新令牌

  • 拦截所有的Web方法返回值
  • 判断是否刷新生成新令牌
    • 检查ThreadLocal中是否保存令牌
    • 把新令牌绑定到R对象中

12 精简返回给客户端的异常内容

12.1 @ControllerAdvice可以全局捕获SpringMVC异常

12.2 判断异常的类型

  • 后端数据验证异常
    • 精简异常内容
  • 未授权异常
    • "你不具有相关权限"
  • EmosException
    • 精简异常内容
  • 普通异常
    • "后端执行异常"

13 本章总结

posted @ 2021-11-05 11:05  小沈曰  阅读(299)  评论(0编辑  收藏  举报