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

JWT(JSON WEB TOKEN)是基于RFC 7519标准定义的一种可以安全传输的小巧和自包含的JSON对象。由于数据是使用数字签名的,所以是可信任的和安全的。JWT可以使用HMAC算法对secret进行加密或者使用RSA的公钥私钥对来进行签名。

JWT通常由头部(Header),负载(Payload),签名(Signature)三个部分组成,中间以.号分隔,其格式为Header.Payload.Signature

Header:声明令牌的类型和使用的算法

  • alg:签名的算法
  • typ:token的类型,比如JWT

Payload:也称为JWT Claims,包含用户的一些信息

系统保留的声明(Reserved claims):

  • iss (issuer):签发人
  • exp (expiration time):过期时间
  • sub (subject):主题
  • aud (audience):受众用户
  • nbf (Not Before):在此之前不可用
  • iat (Issued At):签发时间
  • jti (JWT ID):JWT唯一标识,能用于防止JWT重复使用

公共的声明(public):见 http://www.iana.org/assignments/jwt/jwt.xhtml

私有的声明(private claims):根据业务需要自己定义的数据

Signature:签名

签名格式: HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)

JWT的特点:

  • JWT默认是不加密的,不能把用户敏感类信息放在Payload部分。
  • JWT 不仅可以用于认证,也可以用于交换信息。
  • JWT的最大缺点是服务器不保存会话状态,所以在使用期间不可能取消令牌或更改令牌的权限。
  • JWT本身包含认证信息,为了减少盗用,JWT的有效期不宜设置太长。
  • 为了减少盗用和窃取,JWT不建议使用HTTP协议来传输代码,而是使用加密的HTTPS协议进行传输。
  • 首次生成token比较慢,比较耗CPU,在高并发的情况下需要考虑CPU占用问题。
  • 生成的token比较长,可能需要考虑流量问题。

认证原理:

  • 客户端向服务器申请授权,服务器认证以后,生成一个token字符串并返回给客户端,此后客户端在请求
  • 受保护的资源时携带这个token,服务端进行验证再从这个token中解析出用户的身份信息。

JWT的使用方式:一种做法是放在HTTP请求的头信息Authorization字段里面,格式如下:

Authorization: <token>

需要将服务器设置为接受来自所有域的请求,用Access-Control-Allow-Origin: *

另一种做法是,跨域的时候,JWT就放在POST请求的数据体里面。

对JWT实现token续签的做法:

1、额外生成一个refreshToken用于获取新token,refreshToken需存储于服务端,其过期时间比JWT的过期时间要稍长。

2、用户携带refreshToken参数请求token刷新接口,服务端在判断refreshToken未过期后,取出关联的用户信息和当前token。

3、使用当前用户信息重新生成token,并将旧的token置于黑名单中,返回新的token。

创建用于登录认证的工程auth-service:

1、 创建pom.xml文件

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
 <modelVersion>4.0.0</modelVersion>
 <groupId>com.seasy.springcloud</groupId>
 <artifactId>auth-service</artifactId>
 <version>1.0.0</version>
 <packaging>jar</packaging> 

 <parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>2.0.8.RELEASE</version>
  <relativePath/>
 </parent> 

 <properties>
  <java.version>1.8</java.version>
  <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
 </properties> 

 <dependencies>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
  </dependency>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
  </dependency> 

  <!-- spring cloud -->
  <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
  </dependency> 

  <!-- redis -->
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
  </dependency>
  <dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
  </dependency> 

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

 <dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-dependencies</artifactId>
      <version>Finchley.RELEASE</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
  </dependencies>
 </dependencyManagement>
</project> 

2、JWT工具类

public class JWTUtil {
  public static final String SECRET_KEY = "123456"; //秘钥
  public static final long TOKEN_EXPIRE_TIME = 5 * 60 * 1000; //token过期时间
  public static final long REFRESH_TOKEN_EXPIRE_TIME = 10 * 60 * 1000; //refreshToken过期时间
  private static final String ISSUER = "issuer"; //签发人 

  /**
   * 生成签名
   */
  public static String generateToken(String username){
    Date now = new Date();
    Algorithm algorithm = Algorithm.HMAC256(SECRET_KEY); //算法 

    String token = JWT.create()
      .withIssuer(ISSUER) //签发人
      .withIssuedAt(now) //签发时间
      .withExpiresAt(new Date(now.getTime() + TOKEN_EXPIRE_TIME)) //过期时间
      .withClaim("username", username) //保存身份标识
      .sign(algorithm);
    return token;
  } 

  /**
   * 验证token
   */
  public static boolean verify(String token){
    try {
      Algorithm algorithm = Algorithm.HMAC256(SECRET_KEY); //算法
      JWTVerifier verifier = JWT.require(algorithm)
          .withIssuer(ISSUER)
          .build();
      verifier.verify(token);
      return true;
    } catch (Exception ex){
      ex.printStackTrace();
    }
    return false;
  } 

  /**
   * 从token获取username
   */
  public static String getUsername(String token){
    try{
      return JWT.decode(token).getClaim("username").asString();
    }catch(Exception ex){
      ex.printStackTrace();
    }
    return "";
  }
}

3、LoginController类

@RestController
public class LoginController {
  @Autowired
  StringRedisTemplate redisTemplate; 

  /**
   * 登录认证
   * @param username 用户名
   * @param password 密码
   */
  @GetMapping("/login")
  public AuthResult login(@RequestParam String username, @RequestParam String password) {
    if("admin".equals(username) && "admin".equals(password)){
      //生成token
      String token = JWTUtil.generateToken(username); 

      //生成refreshToken
      String refreshToken = StringUtil.getUUIDString(); 

      //数据放入redis
      redisTemplate.opsForHash().put(refreshToken, "token", token);
      redisTemplate.opsForHash().put(refreshToken, "username", username); 

      //设置token的过期时间
      redisTemplate.expire(refreshToken, JWTUtil.REFRESH_TOKEN_EXPIRE_TIME, TimeUnit.MILLISECONDS); 

      return new AuthResult(0, "success", token, refreshToken);
    }else{
      return new AuthResult(1001, "username or password error");
    }
  } 

  /**
   * 刷新token
   */
  @GetMapping("/refreshToken")
  public AuthResult refreshToken(@RequestParam String refreshToken) {
    String username = (String)redisTemplate.opsForHash().get(refreshToken, "username");
    if(StringUtil.isEmpty(username)){
      return new AuthResult(1003, "refreshToken error");
    } 

    //生成新的token
    String newToken = JWTUtil.generateToken(username);
    redisTemplate.opsForHash().put(refreshToken, "token", newToken);
    return new AuthResult(0, "success", newToken, refreshToken);
  } 

  @GetMapping("/")
  public String index() {
    return "auth-service: " + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
  }
} 

4、application配置信息

spring.application.name=auth-service
server.port=4040 

eureka.instance.hostname=${spring.cloud.client.ip-address}
eureka.instance.instance-id=${spring.cloud.client.ip-address}:${server.port}
eureka.instance.prefer-ip-address=true 

eureka.client.service-url.defaultZone=http://root:123456@${eureka.instance.hostname}:7001/eureka/ 

#redis
spring.redis.database=0
spring.redis.timeout=3000ms
spring.redis.lettuce.pool.max-active=100
spring.redis.lettuce.pool.max-wait=-1ms
spring.redis.lettuce.pool.min-idle=0
spring.redis.lettuce.pool.max-idle=8 

#standalone
spring.redis.host=192.168.134.134
spring.redis.port=7001 

#sentinel
#spring.redis.sentinel.master=mymaster
#spring.redis.sentinel.nodes=192.168.134.134:26379,192.168.134.134:26380 

5、启动类

@SpringBootApplication
@EnableEurekaClient
public class Main{
  public static void main(String[] args){
    SpringApplication.run(Main.class, args);
  }
}

改造SpringCloud Gateway工程

1、在pom.xml文件添加依赖

<!-- redis -->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
  <groupId>org.apache.commons</groupId>
  <artifactId>commons-pool2</artifactId>
</dependency> 

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

2、创建全局过滤器JWTAuthFilter

@Component
public class JWTAuthFilter implements GlobalFilter, Ordered{
  @Override
  public int getOrder() {
    return -100;
  } 

  @Override
  public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    String url = exchange.getRequest().getURI().getPath(); 

    //忽略以下url请求
    if(url.indexOf("/auth-service/") >= 0){
      return chain.filter(exchange);
    } 

    //从请求头中取得token
    String token = exchange.getRequest().getHeaders().getFirst("Authorization");
    if(StringUtil.isEmpty(token)){
      ServerHttpResponse response = exchange.getResponse();
      response.setStatusCode(HttpStatus.OK);
      response.getHeaders().add("Content-Type", "application/json;charset=UTF-8"); 

      Response res = new Response(401, "401 unauthorized");
      byte[] responseByte = JSONObject.fromObject(res).toString().getBytes(StandardCharsets.UTF_8); 

      DataBuffer buffer = response.bufferFactory().wrap(responseByte);
      return response.writeWith(Flux.just(buffer));
    } 

    //请求中的token是否在redis中存在
    boolean verifyResult = JWTUtil.verify(token);
    if(!verifyResult){
      ServerHttpResponse response = exchange.getResponse();
      response.setStatusCode(HttpStatus.OK);
      response.getHeaders().add("Content-Type", "application/json;charset=UTF-8"); 

      Response res = new Response(1004, "invalid token");
      byte[] responseByte = JSONObject.fromObject(res).toString().getBytes(StandardCharsets.UTF_8); 

      DataBuffer buffer = response.bufferFactory().wrap(responseByte);
      return response.writeWith(Flux.just(buffer));
    } 

    return chain.filter(exchange);
  }
} 

3、关键的application配置信息

spring:
 application:
  name: service-gateway
 cloud:
  gateway:
   discovery:
    locator:
     enabled: true
     lowerCaseServiceId: true
   routes:
    #认证服务路由
    - id: auth-service
     predicates:
      - Path=/auth-service/**
     uri: lb://auth-service
     filters:
      - StripPrefix=1

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Asp.Net Core基于JWT认证的数据接口网关实例代码

    前言 近日,应一位朋友的邀请写了个Asp.Net Core基于JWT认证的数据接口网关Demo.朋友自己开了个公司,接到的一个升级项目,客户要求用Aps.Net Core做数据网关服务且基于JWT认证实现对前后端分离的数据服务支持,于是想到我一直做.Net开发,问我是否对.Net Core有所了解?能不能做个简单Demo出来看看?我说,分道扬镳之后我不是调用别人的接口就是提供接口给别人调用,于是便有了以下示例代码. 示例要求能演示获取Token及如何使用该Token访问数据资源,在Demo中实现

  • gateway和jwt网关认证实现过程解析

    这篇文章主要介绍了gateway和jwt网关认证实现过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 思路: 全局过滤器对所有的请求拦截(生成token有效期30分钟,放入redis设置有效期3天.3天之类可以通过刷新接口自动刷新,超过3天需要重新登录.) 前端在调用接口之前先判断token是否过期(3o分钟),过期则先调刷新接口,换取新token, 1引入相关jar <dependency> <groupId>org.sp

  • 详解SpringCloud服务认证(JWT)

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

  • php 后端实现JWT认证方法示例

    JWT是什么 JWT是json web token缩写.它将用户信息加密到token里,服务器不保存任何用户信息.服务器通过使用保存的密钥验证token的正确性,只要正确即通过验证.基于token的身份验证可以替代传统的cookie+session身份验证方法. 它定义了一种用于简洁,自包含的用于通信双方之间以 JSON 对象的形式安全传递信息的方法.JWT 可以使用 HMAC 算法或者是 RSA 的公钥密钥对进行签名.它具备两个特点: 简洁(Compact):可以通过URL, POST 参数或

  • 详解Spring Boot实战之Filter实现使用JWT进行接口认证

    本文介绍了spring Boot实战之Filter实现使用JWT进行接口认证,分享给大家 jwt(json web token) 用户发送按照约定,向服务端发送 Header.Payload 和 Signature,并包含认证信息(密码),验证通过后服务端返回一个token,之后用户使用该token作为登录凭证,适合于移动端和api jwt使用流程 本文示例接上面几篇文章中的代码进行编写,请阅读本文的同时可以参考前面几篇文章 1.添加依赖库jjwt,本文中构造jwt及解析jwt都使用了jjwt库

  • Django JWT Token RestfulAPI用户认证详解

    一般情况下我们Django默认的用户系统是满足不了我们的需求的,那么我们会对他做一定的扩展 创建用户项目 python manage.py startapp users 添加项目apps settings.py INSTALLED_APPS = [ ... 'users.apps.UsersConfig', ] 添加AUTH_USRE_MODEL 替换默认的user AUTH_USER_MODEL = 'users.UserProfile' 如果说想用全局认证需要在配置文件中添加 # 全局认证f

  • 在Angular中使用JWT认证方法示例

    本文介绍了在Angular中使用JWT认证方法示例,分享给大家,具体如下: 项目地址: grading-system 基于session的认证和基于token的认证的方式已经被广泛使用.在session认证中,服务端会存储一份用户登录信息,这份登录信息会在响应时传递给浏览器并保存为Cookie,在下次请求时,会带上这份登录信息,这样就能识别请求来自哪个用户. 在基于session的认证中,每个用户都要生成一份session,这份session通常保存在内存中,随着用户量的增加,服务端的开销会增大

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

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

  • 详解Node.js使用token进行认证的简单示例

    本文只介绍简单的应用,关于json web token的具体介绍以及原理请参考阮一峰老师的JSON Web Token 入门教程. 使用的Node框架是koa2,前端发送ajax请求使用axios 首先创建工程目录: static中存放静态资源,views存放前端模板,server.js为后端代码. 安装必要的依赖项: "dependencies": { "@koa/router": "^8.0.8", "jsonwebtoken&qu

  • Spring Boot详解整合JWT教程

    目录 1.概述 2.优势所在 3.结构组成 3.1.标头(Header) 3.2.有效负载(Payload) 3.3.签名(Signature) 4.Spring boot整合JWT 导入依赖 1.概述 JWT 简称 JSON Web Token,也就是通过JSON形式作为Web应用中的令牌,用于各方之间安全地将信息作为JSON对象传输,在数据传输的过程中还可以完成数据加密.签名等相关处理. 2.优势所在 在JavaWeb阶段,经常使用session来存储,以方便用来判断用户是否操作等等.但这又

  • 详解基于JWT的springboot权限验证技术实现

    JWT简介 Json Web Token(JWT):JSON网络令牌,是为了在网络应用环境间传递声明而制定的一种基于JSON的开放标准((RFC 7519).JWT是一个轻便的安全跨平台传输格式,定义了一个紧凑的自包含的方式用于通信双方之间以 JSON 对象行使安全的传递信息.因为数字签名的存在,这些信息是可信的. 实现步骤: 环境spring boot 1.添加jwt依赖 <dependency> <groupId>com.auth0</groupId> <ar

  • Spring Boot 访问安全之认证和鉴权详解

    目录 拦截器 认证 鉴权 在web应用中有大量场景需要对用户进行安全校,一般人的做法就是硬编码的方式直接埋到到业务代码中,但可曾想过这样做法会导致代码不够简洁(大量重复代码).有个性化时难维护(每个业务逻辑访问控制策略都不相同甚至差异很大).容易发生安全泄露(有些业务可能不需要当前登录信息,但被访问的数据可能是敏感数据由于遗忘而没有受到保护). 为了更安全.更方便的进行访问安全控制,我们可以想到的就是使用springmvc的拦截器(HandlerInterceptor),但其实更推荐使用更为成熟

  • SpringCloud中Gateway实现鉴权的方法

    目录 一.JWT 实现微服务鉴权 1 什么是微服务鉴权 2.代码实现 一.JWT 实现微服务鉴权 JWT一般用于实现单点登录.单点登录:如腾讯下的游戏有很多,包括lol,飞车等,在qq游戏对战平台上登录一次,然后这些不同的平台都可以直接登陆进去了,这就是单点登录的使用场景.JWT就是实现单点登录的一种技术,其他的还有oath2等. 1 什么是微服务鉴权 我们之前已经搭建过了网关,使用网关在网关系统中比较适合进行权限校验. 那么我们可以采用JWT的方式来实现鉴权校验. 2.代码实现 思路分析 1.

  • 浅析k8s中各组件和kube apiserver通信时的认证和鉴权问题

    目录 背景 kubectl的身份和权限 kubectl用的是什么身份? 能操作哪些资源呢? kube-scheduler的身份和权限 kube-scheduler用的是什么身份? kubelet的身份和权限 kubelet用的是什么身份? kubelet能操作哪些资源? 验证kubelet的权限 calico calico用的是什么身份? pod pod用的是什么身份? 总结 背景 和master节点kube api-server通信的组件有很多,包括: kubelet calico sched

  • 详解使用JWT实现单点登录(完全跨域方案)

    首先介绍一下什么是JSON Web Token(JWT)? 官方文档是这样解释的:JSON Web Token(JWT)是一个开放标准(RFC 7519),它定义了一种紧凑且独立的方式,可以在各方之间作为JSON对象安全地传输信息.此信息可以通过数字签名进行验证和信任.JWT可以使用秘密(使用HMAC算法)或使用RSA或ECDSA的公钥/私钥对进行签名. 虽然JWT可以加密以在各方之间提供保密,但只将专注于签名令牌.签名令牌可以验证其中包含的声明的完整性,而加密令牌则隐藏其他方的声明.当使用公钥

  • 详解用Tomcat服务器配置https双向认证过程实战

    工具:keytool (Windows下路径:%JAVA_HOME%/bin/keytool.exe) 环境:Windows8.1企业版.Tomcat-7.0.27.JDK1.6.IE11.Chrome 一.为服务器生成证书 C:\Windows\system32>keytool -genkey -v -alias tomcat -keyalg RSA -keystore D:\tomcat.keystore -validity 36500 输入keystore密码: 再次输入新密码: 您的名字

  • 详解基于Android App 安全登录认证解决方案

    近几年移动互联网的高速发展,智能手机的使用用户呈现爆炸性增长,手机终端上的App 种类繁多,大多数App 都需要与后台系统进行交互,交互的第一步需要进行登录认证,过于简单的认证方式可能被破解从而造成用户信息的泄露甚至威胁着用户的财产安全.为此基于Android 系统,对比现有几种常见的App 登录认证方式,并提出一种采用RSA 非对称加密和加入Token 时效机制的登录认证解决方案.在登录验证阶段采用RSA 非对称加密方式,App 端对服务器端返回的Token 信息加上时间戳,将处理后的Toke

随机推荐