前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >SpringBoot 配合 Jwt 实现请求鉴权

SpringBoot 配合 Jwt 实现请求鉴权

作者头像
MashiroT
发布2023-10-18 15:41:47
2720
发布2023-10-18 15:41:47
举报
文章被收录于专栏:MashiroのBlog

SpringBoot 配合 Jwt 实现请求鉴权

Session 与 JWT

Session

在初学Servlet或Spring时,采用的往往是通过Session来实现登录状态保持以及用户信息的存储,但问题在于Session是有有效期的,当有一段时间不访问后Session就会被服务器销毁(Tomcat默认20min),且会占用服务器内存。

JWT

> JSON Web Token (JWT) is an open standard (RFC 7519) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object. > JWT是RFC 7519的实现方法,用于在各方之间作为JSON对象安全地传输信息。

广义上,JWT是一个标准的名称;狭义上,JWT指的就是用来传递的那个Token字符串。

与Session相比,可以将信息存储在JWT的payload中,只要JWT不过期,则用户的登录状态就不会过期(无状态),当然这也会带来一个问题,即因为JWT无法主动失效,如何控制单用户登录数量。

JWT使用数字签名对上述信息进行加密,可以使用HMAC算法,或是RSA等公私钥来实现。

Jwt.png
Jwt.png

JWT 结构

一个JWT由以下三部分组成:

  • Header
  • Payload
  • Signature

每个部分都表示一个base64编码的字符串,用点('.')作为分隔符分隔。

下面以一个JWT为例: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

第一部分 Header:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 实际上是 JSON

代码语言:javascript
复制
{
  "alg": "HS256",
  "typ": "JWT"
}

在BASE64运算后的结果

同理,第二部分 PAYLOAD:eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ 是 JSON

代码语言:javascript
复制
{
  "sub": "1234567890",
  "name": "John Doe",
  "iat": 1516239022
}

第三部分 VERIFY SIGNATURE: HMACSHA256 使用密钥 your-256-bit-secret 对前两部分的BASE64进行签名 eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ,得到BASE64结果:SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV/adQssw5c=

需要注意的是,这里是对签名之后的结果进行BASE64运算,而不是对结果的16进制表示进行BASE64

使用

引入依赖

代码语言:javascript
复制
<!-- https://mvnrepository.com/artifact/com.auth0/java-jwt -->
<dependency>
    <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>4.4.0</version>
</dependency>

工具类

代码语言:javascript
复制
/**
 * @author MashiroT
 */
public class JwtUtils {
    
    private static final String ALGORITHM_SALT = "SALT";
    private static final int EXPIRATION = 30 * 60 * 1000;

    public static String createToken(Integer uid, String username, Date termStartDate) {
        return JWT.create()
                .withClaim("uid", uid)
                .withClaim("username", username)
                .withExpiresAt(new Date(System.currentTimeMillis() + EXPIRATION))
                .withIssuer("mashirot")
                .withIssuedAt(new Date())
                .sign(Algorithm.HMAC512(ALGORITHM_SALT));
    }

    public static DecodedJWT getVerifier(String authToken) throws JWTVerificationException {
        return JWT.require(Algorithm.HMAC512(ALGORITHM_SALT))
                .withIssuer("mashirot")
                .build()
                .verify(authToken);
    }
}

注解

代码语言:javascript
复制
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface TokenRequired {
    boolean required() default true;
}

在需要鉴权的Controller方法上添加此注解

拦截器类

代码语言:javascript
复制
@Configuration
public class AuthInterceptorConfig implements WebMvcConfigurer {

    @Bean
    public AuthInterceptor authInterceptorFactory() {
        return new AuthInterceptor();
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(authInterceptorFactory())
                .addPathPatterns("/**");
    }
}
代码语言:javascript
复制
@Component
public class AuthInterceptor implements HandlerInterceptor {

    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 放行 OPTIONS 请求
        if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
            return true;
        }
        // 判断是否请求Controller的方法
        if (!(handler instanceof HandlerMethod)) {
            return true;
        }
        String authToken = request.getHeader("Authorization");
        Method method = ((HandlerMethod) handler).getMethod();
        if (method.isAnnotationPresent(TokenRequired.class)) {
            TokenRequired tokenRequired = method.getAnnotation(TokenRequired.class);
            // 判断是否需要鉴权
            if (tokenRequired.required()) {
                if (authToken == null) {
                    // 返回失败结果
            response.getWriter().write(OBJECT_MAPPER.writeValueAsString(Result.failed(StatusCodeConstants.AUTH_VERIFY_FAILED, null)));
                    return false;
                }
                // Authorization: Bearer XXXXXX
                try {
                    authToken = authToken.split(" ")[1];
                    DecodedJWT decodedJwt = JwtUtils.getVerifier(authToken);
                    Integer uid = decodedJwt.getClaim("uid").asInt();
                    String username = decodedJwt.getClaim("username").asString();
                    request.getSession().setAttribute("uid", uid);
                    request.getSession().setAttribute("username", username);
                } catch (Exception e) {
                    response.getWriter().write(OBJECT_MAPPER.writeValueAsString(Result.failed(StatusCodeConstants.AUTH_VERIFY_FAILED, null)));
                    return false;
                }
            }
            return true;
        }
        return true;
    }
}

<br /><br /><br /> 参考:

  1. https://www.baeldung.com/java-auth0-jwt
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2023-07-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • SpringBoot 配合 Jwt 实现请求鉴权
    • Session 与 JWT
      • Session
      • JWT
    • JWT 结构
      • 使用
        • 引入依赖
        • 工具类
        • 注解
        • 拦截器类
    相关产品与服务
    对象存储
    对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档