Spring Cloud Gateway 使用JWT工具类做用户登录校验功能

1. JWT测试

/**
 * @Auther: csp1999
 * @Date: 2021/01/24/19:29
 * @Description: JWT测试
 */
public class JwtTest {

 /**
  * 创建Jwt令牌:
  *
  * JWT = 头部Header + 载荷playload + 签名signature
  */
 @Test
 public void testCreateJwt() {
  // 构建jwt令牌
  // 1.头部Header: 描述关于该JWT的最基本的信息,例如其类型以及签名所用的算法等
  JwtBuilder builder = Jwts.builder()
    .setId("8989")      // 设置令牌唯一编号
    .setIssuer("csp1999")    // 设置令牌颁发者
    .setSubject("JWT加密测试")   // 设置令牌主题 可以是JSON数据
    .setIssuedAt(new Date())   // 设置令牌签发日期
    .setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 3));// 设置令牌过期时间 3分钟

  // 2.自定义载荷playload: 存放有效信息的地方
  Map<String,Object> userInfo = new HashMap<>();
  userInfo.put("username","csp");
  userInfo.put("password","123456");
  userInfo.put("school","河南科技大学");
  userInfo.put("age","22");
  // 将载荷添加到JWT令牌中
  builder.addClaims(userInfo);

  // 3.为令牌设置 签名signature
  builder.signWith(SignatureAlgorithm.HS256, "haust");// 设置令牌的签名 使用HS256算法,并设置SecretKey密钥(字符串)

  // 构建 并返回一个字符串
  String jwtStr = builder.compact();
  System.out.println(jwtStr);
 }

 /**
  * 解析Jwt令牌数据
  */
 @Test
 public void testParseJwt() {
  // jwt字符串
  String jwtStr = "eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI4OTg5IiwiaXNzIjoiY3NwMTk5OSIsInN1YiI6IkpXVOWKoOWvhua1i-ivlSIsImlhdCI6MTYxMTQ4ODc1MSwiZXhwIjoxNjExNDg4OTMxLCJwYXNzd29yZCI6IjEyMzQ1NiIsInNjaG9vbCI6Iuays-WNl-enkeaKgOWkp-WtpiIsImFnZSI6IjIyIiwidXNlcm5hbWUiOiJjc3AifQ.uH28G9MSHfzaKBAOyr8AdksYLVvy8O5P8g7TORZIUFY";

  // 解析jwt字符串
  Claims claims = Jwts.parser().
    setSigningKey("haust").  // 密钥(盐)
    parseClaimsJws(jwtStr).  // 要解析的令牌对象
    getBody();     // 获取解析后的结果

  // {jti=8989, iss=csp1999, sub=JWT加密测试, iat=1611488751, exp=1611488931, password=123456, school=河南科技大学, age=22, username=csp}
  System.out.println(claims);
 }
}

2. JWT工具类

/**
 * @Auther: csp1999
 * @Date: 2021/01/24/19:29
 * @Description: JWT工具类
 */
public class JwtUtil {
 // 有效期为
 public static final Long JWT_TTL = 3600000L;// 60 * 60 * 1000 一个小时

 // Jwt令牌信息
 public static final String JWT_KEY = "itcast";

 /**
  * 生成令牌
  * @param id
  * @param subject
  * @param ttlMillis
  * @return
  */
 public static String createJWT(String id, String subject, Long ttlMillis) {
  // 指定算法
  SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;

  // 当前系统时间
  long nowMillis = System.currentTimeMillis();
  // 令牌签发时间
  Date now = new Date(nowMillis);

  // 如果令牌有效期为null,则默认设置有效期1小时
  if (ttlMillis == null) {
   ttlMillis = JwtUtil.JWT_TTL;
  }

  // 令牌过期时间设置
  long expMillis = nowMillis + ttlMillis;
  Date expDate = new Date(expMillis);

  // 生成秘钥
  SecretKey secretKey = generalKey();

  // 封装Jwt令牌信息
  JwtBuilder builder = Jwts.builder()
    .setId(id)         //唯一的ID
    .setSubject(subject)      // 主题 可以是JSON数据
    .setIssuer("admin")       // 签发者
    .setIssuedAt(now)       // 签发时间
    .signWith(signatureAlgorithm, secretKey) // 签名算法以及密匙
    .setExpiration(expDate);     // 设置过期时间

  return builder.compact();
 }

 /**
  * 生成加密 secretKey
  *
  * @return
  */
 public static SecretKey generalKey() {
  byte[] encodedKey = Base64.getEncoder().encode(JwtUtil.JWT_KEY.getBytes());
  SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
  return key;
 }

 /**
  * 解析令牌数据
  *
  * @param jwt
  * @return
  * @throws Exception
  */
 public static Claims parseJWT(String jwt) throws Exception {
  SecretKey secretKey = generalKey();
  return Jwts.parser()
    .setSigningKey(secretKey)
    .parseClaimsJws(jwt)
    .getBody();
 }

 public static void main(String[] args) {
  String jwt = JwtUtil.createJWT("weiyibiaoshi", "aaaaaa", null);

  System.out.println(jwt);
  try {
   Claims claims = JwtUtil.parseJWT(jwt);
   System.out.println(claims);
  } catch (Exception e) {
   e.printStackTrace();
  }
 }
}

3. 用户登录校验

3.1 网关过滤器

/**
 * @Auther: csp1999
 * @Date: 2021/01/24/20:17
 * @Description: 授权过滤器
 */
@Component
public class AuthorizeFilter implements GlobalFilter, Ordered {

 // 令牌头名字
 private static final String AUTHORIZE_TOKEN = "Authorization";

 /**
  * 全局过滤器
  *
  * @param exchange
  * @param chain
  * @return
  */
 @Override
 public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
  // 获取Request、Response对象
  ServerHttpRequest request = exchange.getRequest();
  ServerHttpResponse response = exchange.getResponse();

  // 获取请求的URI
  String path = request.getURI().getPath();

  // 如果是登录、goods等开放的微服务[这里的goods部分开放],则直接放行,这里不做完整演示,完整演示需要设计一套权限系统
  // 未登录下只放行登录和搜索
  if (path.startsWith("/api/user/login") || path.startsWith("/api/brand/search/")) {
   // 放行
   Mono<Void> filter = chain.filter(exchange);

   return filter;
  }

  // 从头文件中获取的令牌信息
  String token = request.getHeaders().getFirst(AUTHORIZE_TOKEN);
  // 如果为true:说明令牌在头文件中, false:令牌不在头文件中,将令牌封装入头文件,再传递给其他微服务
  boolean hasToken = true;

  // 如果头文件中没有令牌信息,则从请求参数中获取
  if (StringUtils.isEmpty(token)) {
   token = request.getQueryParams().getFirst(AUTHORIZE_TOKEN);
   hasToken = false;
  }

  // 如果为空,则输出错误代码
  if (StringUtils.isEmpty(token)) {
   // 设置方法不允许被访问,405错误代码
   response.setStatusCode(HttpStatus.METHOD_NOT_ALLOWED);
   return response.setComplete();
  }

  // 如果不为空,则解析令牌数据
  try {
   Claims claims = JwtUtil.parseJWT(token);
  } catch (Exception e) {
   e.printStackTrace();
   // 解析失败,响应401错误
   response.setStatusCode(HttpStatus.UNAUTHORIZED);
   return response.setComplete();
  }

  // 放行之前,将令牌封装到头文件中(这一步是为了方便AUTH2校验令牌)
  request.mutate().header(AUTHORIZE_TOKEN,token);

  // 放行
  return chain.filter(exchange);
 }

 /**
  * 过滤器执行顺序
  *
  * @return
  */
 @Override
 public int getOrder() {
  // 首位
  return 0;
 }
}

3.2 网关微服务application.yml

spring:
 cloud:
 gateway:
  globalcors:
  corsConfigurations:
   '[/**]': # 匹配所有请求
   allowedOrigins: "*" # 跨域处理 允许所有的域
   allowedMethods: #支持的请求类型
    - GET
    - POST
    - PUT
    - DELETE
  routes:
  # 对接商品goods微服务路由相关配置
  - id: changgou_goods_route
   uri: lb://changgou-goods
   predicates:
   - Path=/api/brand/**,/api/category/**
   filters:
   - StripPrefix=1
   - name: RequestRateLimiter # 请求数限流 名字不能随便写 ,使用默认的facatory
    args:
    # 用于限流的键的解析器的 Bean 对象的名字。它使用 SpEL 表达式根据#{@beanName}从 Spring 容器中获取 Bean 对象。
    key-resolver: "#{@ipKeyResolver}"
    # 令牌桶每秒填充平均速率
    redis-rate-limiter.replenishRate: 1
    # 令牌桶总容量
    redis-rate-limiter.burstCapacity: 1
    # 上面配置,表示1秒内,允许 1个请求通过,令牌桶的填充速率也是1秒钟添加1个令牌。
  # 对接用户user微服务路由相关配置
  - id: changgou_user_route
   uri: lb://changgou-user
   predicates:
   - Path=/api/user/**,/api/address/**,/api/areas/**,/api/cities/**,/api/provinces/**
   filters:
   # user微服务真实请求中是没有/api的,所以这里StripPrefix=1
   - StripPrefix=1
 # 微服务名称
 application:
 name: changgou-gateway-web
 # Redis配置
 redis:
 # Redis数据库索引(默认为0)
 database: 0
 # Redis服务器地址
 host: 8.131.66.136
 # Redis服务器连接端口
 port: 6379
 # Redis服务器连接密码(默认为空)
 password: csp19990129

server:
 port: 8001
eureka:
 client:
 service-url:
  defaultZone: http://127.0.0.1:7001/eureka
 instance:
 prefer-ip-address: true
management:
 endpoint:
 gateway:
  enabled: true
 web:
  exposure:
  include: true

3.3 网关微服务主启动类

/**
 * @Auther: csp1999
 * @Date: 2021/01/24/15:16
 * @Description: 用户/前台微服务网关启动类
 */
@SpringBootApplication
@EnableEurekaClient
public class GatewayWebApplication {

 public static void main(String[] args) {
  SpringApplication.run(GatewayWebApplication.class, args);
 }

 /**
  * IP限流:由用户请求的IP创建创建用户唯一标识,进而根据IP进行限流操作
  *
  * @return
  */
 @Bean(name = "ipKeyResolver")
 public KeyResolver userKeyResolver() {
  return new KeyResolver() {
   @Override
   public Mono<String> resolve(ServerWebExchange exchange) {
    // 获取远程客户端IP
    String hostName = exchange.getRequest().getRemoteAddress().getAddress().getHostAddress();
    System.out.println("hostName:" + hostName);
    return Mono.just(hostName);
   }
  };
 }
}

3.4 用户微服务编写登录代码

/**
 * @Author: csp1999
 * @Description: User 的Controller
 * @Date 2021/1/14 0:18
 */
@RestController
@RequestMapping("/user")
@CrossOrigin
public class UserController {

 @Autowired
 private UserService userService;

	 /***
  * 修改User数据
  * @param user
  * @param id
  * @return
  */
 @PutMapping(value = "/{id}")
 public Result update(@RequestBody User user, @PathVariable String id) {
  	 ...
 }

 /***
  * 新增User数据
  * @param user
  * @return
  */
 @PostMapping
 public Result add(@RequestBody User user) {
  ...
 }

 /***
  * 根据ID查询User数据
  * @param id
  * @return
  */
 @GetMapping("/{id}")
 public Result<User> findById(@PathVariable String id) {
  ...
 }

 /***
  * 查询User全部数据
  * @return
  */
 @GetMapping
 public Result<List<User>> findAll() {
  ...
 }

 /***
  * 用户登录
  * @param username
  * @param password
  * @param response
  * @param request
  * @return
  */
 @RequestMapping("/login")
 public Result<User> login(String username, String password, HttpServletResponse response, HttpServletRequest request) {
  // 1.从数据库中查询用户名对应的用户的对象
  User user = userService.findById(username);
  if (user == null) {
   // 2.判断用户是否为空 为空返回数据
   return new Result<User>(false, StatusCode.LOGINERROR, "用户名或密码错误...");
  }

  // 3.如果不为空 判断密码是否正确 若正确 则登录成功
  if (BCrypt.checkpw(password, user.getPassword())) {
   // 登录成功,讲用户信息存入map
   Map<String, Object> info = new HashMap<String, Object>();
   info.put("role", "USER");
   info.put("success", "SUCCESS");
   info.put("username", username);

   // 3.1生成令牌
   String jwt = JwtUtil.createJWT(UUID.randomUUID().toString(), JSON.toJSONString(info), null);
   // 3.2设置jwt存入 cookie 中
   Cookie cookie = new Cookie("Authorization", jwt);
   response.addCookie(cookie);
   // 3.3设置jwt存入头文件中
   response.setHeader("Authorization", jwt);

   return new Result<User>(true, StatusCode.OK, "登录成功", jwt);
  } else {
   // 登录失败
   return new Result<User>(false, StatusCode.LOGINERROR, "用户名或密码错误");
  }
 }
}

到此这篇关于Spring Cloud Gateway 使用JWT工具类做用户登录校验的示例代码的文章就介绍到这了,更多相关Spring Cloud Gateway 用户登录校验内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

时间: 2021-01-25

SpringCloud Finchley Gateway 缓存请求Body和Form表单的实现

在接入Spring-Cloud-Gateway时,可能有需求进行缓存Json-Body数据或者Form-Urlencoded数据的情况. 由于Spring-Cloud-Gateway是以WebFlux为基础的响应式架构设计,所以在原有Zuul基础上迁移过来的过程中,传统的编程思路,并不适合于Reactor Stream的开发. 网络上有许多缓存案例,但是在测试过程中出现各种Bug问题,在缓存Body时,需要考虑整体的响应式操作,才能更合理的缓存数据 下面提供缓存Json-Body数据或者Form

基于Nacos实现Spring Cloud Gateway实现动态路由的方法

简介 该文档主要介绍以Nacos为配置中心,实现Spring Cloud GateWay 实现动态路由的功能.Spring Cloud Gateway启动时候,就将路由配置和规则加载到内存里,无法做到不重启网关就可以动态的对应路由的配置和规则进行增加,修改和删除.通过nacos的配置下发的功能可以实现在不重启网关的情况下,实现动态路由. 集成 Spring Cloud GateWay集成 spring-cloud-starter-gateway:路由转发.请求过滤(权限校验.限流以及监控等) s

详解用JWT对SpringCloud进行认证和鉴权

JWT(JSON WEB TOKEN)是基于RFC 7519标准定义的一种可以安全传输的小巧和自包含的JSON对象.由于数据是使用数字签名的,所以是可信任的和安全的.JWT可以使用HMAC算法对secret进行加密或者使用RSA的公钥私钥对来进行签名. JWT通常由头部(Header),负载(Payload),签名(Signature)三个部分组成,中间以.号分隔,其格式为Header.Payload.Signature Header:声明令牌的类型和使用的算法 alg:签名的算法 typ:to

Spring Cloud Gateway使用Token验证详解

引入依赖 <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <ty

详解SpringCloud服务认证(JWT)

 - JWT JWT(JSON Web Token), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519).该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景.JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密. - JWT与其它的区别 通常情况下,把API直接暴露出去是风险很大的,不说别的

springboot2.0和springcloud Finchley版项目搭建(包含eureka,gateWay,Freign,Hystrix)

前段时间spring boot 2.0发布了,与之对应的spring cloud Finchley版本也随之而来了,两者之间的关系和版本对应详见我这边文章:spring boot和spring cloud对应的版本关系 项目地址:spring-cloud-demo spring boot 1.x和spring cloud Dalston和Edgware版本搭建的微服务项目现在已经很流行了,现在很多企业都已经在用了,这里就不多说了. 使用版本说明: spring boot 2.0.x spring

SpringBoot2.0整合SpringCloud Finchley @hystrixcommand注解找不到解决方案

hystrix参数使用方法 通过注解@HystrixCommand的commandProperties去配置, 如下就是hystrix命令超时时间命令执行超时时间,为1000ms和执行是不启用超时 @RestController public class MovieController { @Autowired private RestTemplate restTemplate; @GetMapping("/movie/{id}") @HystrixCommand(commandPro

SpringBoot2.0 整合 Dubbo框架实现RPC服务远程调用方法

一.Dubbo框架简介 1.框架依赖 图例说明: 1)图中小方块 Protocol, Cluster, Proxy, Service, Container, Registry, Monitor 代表层或模块,蓝色的表示与业务有交互,绿色的表示只对 Dubbo 内部交互. 2)图中背景方块 Consumer, Provider, Registry, Monitor 代表部署逻辑拓扑节点. 3)图中蓝色虚线为初始化时调用,红色虚线为运行时异步调用,红色实线为运行时同步调用. 4)图中只包含 RPC

ionic 3.0+ 项目搭建运行环境的教程

本文介绍了基于ionic3.4.0的项目搭建,分享给大家,具体如下: 在写这篇文章的时候,ionic版本已经更新到3.4.0.手头上有一大堆ionic1.x版本的项目,也在这个基础上开发了许多第三方的插件.实在是按捺不住迭代重构的想法,终于开始了3.x的升级.文中的内容都是在实践的过程编写的,注意请不要着急升级版本. 环境迁移 先看下升级后最新的环境输出信息 全局升级了Cordova和Ionic的版本,分别是7.0.1和3.4.0.输出ionic info 打印出最新的环境配置信息.这里要特殊指

SpringCloud Finchley+Spring Boot 2.0 集成Consul的方法示例(1.2版本)

概述: Spring Boot 2.0相对于之前的版本,变化还是很大的.首先对jdk的版本要求已经不能低于1.8,其次依赖的spring的版本也是最新版本5.0,并集成了功能强大的webflux等. SpringCloud Finchley 版本的升级也带来了全新组件:Spring Cloud Function 和 Spring Cloud Gateway ,前者致力于函数式编程模块的整合,后者则是网关netflix zuul 的替换组件. 1)需要的依赖: <?xml version="

springboot2.0+elasticsearch5.5+rabbitmq搭建搜索服务的坑

前一阵子准备为项目搭建一个简单的搜索服务,虽然业务数据库mongodb提供了文本搜索的支持,但是在大量文档需要通过关键词进行定位时,es明显更加适合去作为一个搜索引擎(虽然我们之前大部分使用到了ELK那套分析和可视化的特性).Elasticsearch建立在Lucene之上并且支持极其快速的查询和丰富的查询语法,偶尔也可以作为一个轻量级的NoSQL.但是对复杂查询和聚合操作的能力并不是很强. 本篇不会提及如何搭建一个简单搜索服务,而是记录一下大约一周工作时间内遇见的几个坑.. 为什么选择elas

Vue.js系列之项目搭建(1)

说明: 我们项目现在用的是:vue2.0 + vue-cli + webpack + vue-router2.0 + vue-resource1.0.3 如果大家在实践的过程中与本文所说的内容有较大区别的话看看是不是版本问题. 本文是一系列文章,在我对Vue有了更深刻的理解认识之后会对文章及时进行修改或更正.欢迎大家批评指出错误. 今天要讲讲Vue2.0了.最近将公司App3.0用vue2.0构建了一个web版,因为是第一次使用vue,而且一开始使用的时候2.0出来一个月不到,很多坑都是自己去踩

Python之Web框架Django项目搭建全过程

Python之Web框架Django项目搭建全过程 IDE说明: Win7系统 Python:3.5 Django:1.10 Pymysql:0.7.10 Mysql:5.5 注:可通过pip freeze查看已安装库版本信息. Django 是由 Python 开发的一个免费的开源网站框架,可以用于快速搭建高性能,优雅的网站! Django 特点 强大的数据库功能 用python的类继承,几行代码就可以拥有一个丰富,动态的数据库操作接口(API),如果需要你也能执行SQL语句. 自带的强大的后

requirejs + vue 项目搭建详解

以前都是支持 司徒正美 的,毕竟咱们也是跟着 司徒正美 一起走进了前端的世界.所以一般MVVM都是用avalon的,当然也是考虑到项目需要支持IE6,7,8的考虑.当然在用的时候也有一些小坑和bug,都处理了.今年正美正好升级avalon2.0,加入虚拟dom的时候,不稳定了,就考试寻找其他的mvvm框架. 看了比较流行的一些框架,最后选择了vue.选择他的原因是 文档比较全,而且还有中文的(你懂的),生态圈比较好,有vux, vue-loader, vue-router ,组件化设计的也很好.

vue.js的手脚架vue-cli项目搭建的步骤

手脚架是什么? 众所周知,现在的前端项目发展得越渐越大,我们前端程序员要从0开始去搭建一套完整的项目很费时,所以这时候前端工程的手脚架就出现了. 我用得vue-cli也是其中之一,还有其他的我也说不清,我就说一下我接触比较多的这款vue-cli, 但是所有手脚架的核心:都是为了能够快速搭建一个完整的项目的结构,开发者只需要在生成的项目结构的基础上进行开发即可,非常简单高效. 然后, vue-cli 的脚手架项目模板有browserify 和 webpack ,vue-lic这个手脚架是把预定义的