实现 jwt 鉴权- SpringBoot + 微服务

目录

项目结构

主要步骤

auth-service里:

[1. 配置 pom.xml 依赖](#1. 配置 pom.xml 依赖)

[2. 实现HandlerInterceptor 接口的 preHandle 函数](#2. 实现HandlerInterceptor 接口的 preHandle 函数)

[3. 实现 WebMvcConfigurer 的 addInterceptors 接口](#3. 实现 WebMvcConfigurer 的 addInterceptors 接口)

[4. 生成 token 和验证 token](#4. 生成 token 和验证 token)

[5. 登录接口示例](#5. 登录接口示例)

[user-service 里:](#user-service 里:)

[6. 实现拦截:以 user-service 为例](#6. 实现拦截:以 user-service 为例)


ps:项目源码等该项目课程答辩结束后会贴出,到时候可以看看源码会更清晰

项目结构

以 jwt 鉴权服务auth-service和 user-service 用户服务为例,auth-service 和 user-service 两个项目位于目录同级,项目结构如下

主要步骤

auth-service里:

1. 配置 pom.xml 依赖
复制代码
<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
        </dependency>
        <dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>3.10.0</version>
        </dependency>

        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>2.5</version>
        </dependency>

        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
        </dependency>
    </dependencies>
2. 实现HandlerInterceptor 接口的 preHandle 函数
    • 注意加 @Component

    • preHandle 会拦截指定路径的请求,若验证成功返回了 return true ,然后该接口自己会再转发到对应接口处理请求,如果我们return false了就不会继续转发请求

      package com.example.authservice.interceptors;

      import com.auth0.jwt.exceptions.AlgorithmMismatchException;
      import com.auth0.jwt.exceptions.SignatureVerificationException;
      import com.auth0.jwt.exceptions.TokenExpiredException;
      import com.example.authservice.utils.JWTUtils;
      import com.fasterxml.jackson.databind.ObjectMapper;
      import org.springframework.stereotype.Component;
      import org.springframework.web.servlet.HandlerInterceptor;
      import javax.servlet.http.HttpServletRequest;
      import javax.servlet.http.HttpServletResponse;
      import java.util.HashMap;
      import java.util.Map;

      @Component
      public class JWTInterceptor implements HandlerInterceptor {

      复制代码
      @Override
      public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
          System.out.println("*****************************************");
          System.out.println("开始token验证");
          System.out.println("*****************************************");
          Map<String, Object> map = new HashMap<>();
          // 获取请求头中令牌
          String token = request.getHeader("token");
          try {
              // 验证令牌
              JWTUtils.verify(token);
              System.out.println("*****************************************");
              System.out.println("token验证成功");
              System.out.println("*****************************************");
              return true;
          } catch (SignatureVerificationException e) {
              e.printStackTrace();
              map.put("msg","无效签名!");
          }catch (TokenExpiredException e){
              e.printStackTrace();
              map.put("msg","token过期!");
          }catch (AlgorithmMismatchException e){
              e.printStackTrace();
              map.put("msg","token算法不一致!");
          }catch (Exception e){
              e.printStackTrace();
              map.put("msg","token无效!!");
          }
          System.out.println("*****************************************");
          System.out.println("未通过token验证");
          System.out.println("*****************************************");
          // 未通过验证:设置状态
          map.put("state",false);
          // 将map转为json  jackson
          String json = new ObjectMapper().writeValueAsString(map);
          response.setContentType("application/json;charset=UTF-8");
          response.getWriter().println(json);
          return false;
      }

      }

3. 实现 WebMvcConfigurer 的 addInterceptors 接口
  • 注册前面写的拦截器
    • 指定拦截路径 .addPathPatterns 和不拦截路径 .excludePathPatterns
  • 注意:
    • 拦截所有路径要用**/****------两个*,不要写成一个了!!!
    • 记得加 @Configuration
  • 一般登录接口不拦截
    • 登录接口可以也写在 auth-service 服务中,更方便

      • ps:我本来写在 user-service 里的,本地运行也没问题,但是当我 maven 打包时一直会出现找不到 auth-service 这个模块(具体是 login 接口需要调用JWTUtils 的接口进行 token 验证,这里一直报错找不到这个 import),可是我明明 user-service 里已经添加依赖了,试了网上方法都不行。于是放弃,直接将 login 接口写在 auth-service 里,就避免了在 user-service 里直接调用 suth-service 的接口,但还是需要添加 suth-service 依赖的,因为要实现请求拦截,后面也会说到,这个时候再 maven 打包就可以了,有点玄学在的、、、

      package com.example.authservice.config;

      import com.example.authservice.interceptors.JWTInterceptor;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.context.annotation.Configuration;
      import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
      import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

      @Configuration
      public class InterceptorConfig implements WebMvcConfigurer {
      @Autowired
      private JWTInterceptor jWTInterceptor;

      复制代码
      @Override
      public void addInterceptors(InterceptorRegistry registry) {
          registry.addInterceptor(jWTInterceptor)
                  .addPathPatterns("/user/**")
                  .excludePathPatterns("/auth/login");
      }

      }

4. 生成 token 和验证 token
  • 此处 SHA256 加密

  • getToken(Map<String,String> map)函数生成 token,代码注释写了,一个萝卜一个坑按需填空即可

  • 验证 token 合法性的函数照抄就好

    package com.example.authservice.utils;

    import com.auth0.jwt.JWT;
    import com.auth0.jwt.JWTCreator;
    import com.auth0.jwt.algorithms.Algorithm;
    import com.auth0.jwt.interfaces.DecodedJWT;

    import java.util.Calendar;
    import java.util.Map;

    public class JWTUtils {
    private static final String SIGNATURE = "!@#$SGW^HDY*%G";

    复制代码
      /**
       * 生成token
       */
      public static String getToken(Map<String,String> map){
          // 默认7天过期
          Calendar instance = Calendar.getInstance();
          instance.add(Calendar.DATE,7);
          // 创建jwt builder
          JWTCreator.Builder builder = JWT.create();
          // 设置payload
          map.forEach(builder::withClaim);
          // 指定令牌过期时间
          return builder.withExpiresAt(instance.getTime())
                  .sign(Algorithm.HMAC256(SIGNATURE));
      }
    
      /**
       * 验证token合法性
       */
      public static DecodedJWT verify(String token){
          return JWT.require(Algorithm.HMAC256(SIGNATURE)).build().verify(token);
      }

    }

5. 登录接口示例

写在 auth-service 里,主要逻辑就是在 controller 层,示例如下

复制代码
package com.example.authservice.controller;

import com.example.authservice.utils.JWTUtils;
import com.example.authservice.service.AuthService;
import io.swagger.annotations.Api;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

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

@Api(tags = {"登录鉴权接口文档"})
@RestController
@RequestMapping("/auth")
@EnableSwagger2
public class AuthController {

    @Autowired
    private AuthService authService;

    @PostMapping("/login")
    @ResponseBody
    public Map<String,Object> login(String SID, String SPassword) {
        Map<String, Object> result = new HashMap<>();
        if(authService.login(SID, SPassword)){
            Map<String, String> payLoad = new HashMap<>();
            payLoad.put("id", SID);
            String token = JWTUtils.getToken(payLoad);
            result.put("token", token);
            result.put("state", true);
            result.put("msg", "登录成功!");
        }
         else {
            result.put("state","false");
            result.put("msg", "登录失败!");
        }
        return result;
    }
}

user-service 里:

6. 实现拦截:以 user-service 为例

要实现拦截需要以下步骤:

  • user-service 的 pom.xml 里配置 auth-service 依赖

    复制代码
      <dependencies>
    
          <dependency>
              <groupId>org.springframework.boot</groupId>
              <artifactId>spring-boot-starter-web</artifactId>
          </dependency>
          <!--JWT-->
          <dependency>
              <groupId>com.auth0</groupId>
              <artifactId>java-jwt</artifactId>
              <version>3.10.0</version>
          </dependency>
    
          <!-- 添加对authservice模块的依赖 -->
          <dependency>
              <groupId>com.example</groupId>
              <artifactId>auth-service</artifactId>
              <version>0.0.1-SNAPSHOT</version>
          </dependency>
          <!-- 排除继承自authservice模块的该依赖,否则会冲突-->
          <dependency>
              <groupId>javax.servlet</groupId>
              <artifactId>servlet-api</artifactId>
              <version>2.5</version>
              <exclusions>
                  <exclusion>
                      <groupId>javax.servlet</groupId>
                      <artifactId>servlet-api</artifactId>
                  </exclusion>
              </exclusions>
          </dependency>
    
    
      </dependencies>
  • 服务启动类上把 auth-service 加上,这样才能扫描到这个包,但是注意,@SpringBootApplication 本来是默认扫描该启动类对应的包的,如果加了指定扫描包选项,那么也要把这个启动类自己对应的那个包写上,不然自己都扫描不到了,即:

    @SpringBootApplication(scanBasePackages={"com.example.userservice", "com.example.authservice"})

参考:

jwt 鉴权参考:

深入浅出JWT的token鉴权机制_jwtutils.token_header-CSDN博客

FrameWork/springbootjwt at master · EamonHu/FrameWork · GitHub

拦截器不起作用看这篇:

Spring Boot 拦截器无效,不起作用_判断拦截器是否起作用-CSDN博客

相关推荐
想躺平的咸鱼干1 分钟前
Volatile解决指令重排和单例模式
java·开发语言·单例模式·线程·并发编程
hqxstudying29 分钟前
java依赖注入方法
java·spring·log4j·ioc·依赖
·云扬·37 分钟前
【Java源码阅读系列37】深度解读Java BufferedReader 源码
java·开发语言
martinzh2 小时前
Spring AI 项目介绍
后端
Bug退退退1232 小时前
RabbitMQ 高级特性之重试机制
java·分布式·spring·rabbitmq
小皮侠2 小时前
nginx的使用
java·运维·服务器·前端·git·nginx·github
前端付豪2 小时前
20、用 Python + API 打造终端天气预报工具(支持城市查询、天气图标、美化输出🧊
后端·python
爱学习的小学渣2 小时前
关系型数据库
后端
武子康2 小时前
大数据-33 HBase 整体架构 HMaster HRegion
大数据·后端·hbase
前端付豪2 小时前
19、用 Python + OpenAI 构建一个命令行 AI 问答助手
后端·python