详解用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

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

时间: 2019-03-15

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

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

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

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

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

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

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

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

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

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

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

详解SpringCloud服务认证(JWT)

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

详解SpringCloud微服务之Rest

目录 一.什么是RestTemplate? 二.四种请求方式 2.1 GET请求 2.2 POST请求 2.3 PUT请求 2.4 DELETE请求 一.什么是RestTemplate? RestTemplate 是一个HTTP客户端,在Spring Cloud的服务调用方使用它我们可以方便的调用HTTP接口,支持GET.POST.PUT.DELETE等方法. 二.四种请求方式 首先注入Bean对象 @Configuration public class MyConfig { @Bean pub

详解微服务架构及其演进史

目录 1 传统单体系统介绍 1.1 单体系统的问题 1.2 单体系统的优点 1.3 单体服务到微服务的发展过程 2 关于微服务 2.1 单一职责 2.2 轻量级通信 2.3 独立性 2.4 进程隔离 2.5 混合技术栈和混合部署方式 2.6 简化治理 2.7 安全可靠,可维护. 3 微服务演进史 3.1 第一阶:简单服务通信模块 3.2 第二阶:原始通信时代 3.3 第三阶:TCP时代 3.4 第四阶:第一代微服务(Spring Cloud/RPC) 3.5 第五阶:第二代微服务 3.6 第六阶

详解SpringCloud新一代网关Gateway

一.概述简介 1.1.简介 SpringCloud Gateway作为Spring Cloud生态系统中的网关,目标是替代Zuul,在Spring Cloud 2.0以上版本中,没有对新版本的Zuul 2.0以上最新高性能版本进行集成,仍然还是使用的Zuul 1.x非Reactor模式的老版本.而为了提升网关的性能,SpringCloud Gateway是基于WebFlux框架实现的,而WebFlux框架底层则使用了高性能的Reactor模式通信框架Netty. Spring Cloud Gat

详解SpringCloud的负载均衡

目录 一.什么是负载均衡 二.负载均衡的简单分类 三.为什么需要做负载均衡 四.springCloud如何开启负载均衡 五.IRule 1.RandomRule:表示随机策略,它将从服务清单中随机选择一个服务: 2.ClientConfigEnabledRoundRobinRule:ClientConfigEnabledRoundRobinRule并没有实现什么特殊的处理逻辑,但是他的子类可以实现一些高级策略, 当一些本身的策略无法实现某些需求的时候,它也可以做为父类帮助实现某些策略,一般情况下

Django 自定义权限管理系统详解(通过中间件认证)

1. 创建工程文件, 修改setting.py文件 django-admin.py startproject project_name 特别是在 windows 上,如果报错,尝试用 django-admin 代替 django-admin.py 试试 setting.py 最终的配置文件 import os import sys # Build paths inside the project like this: os.path.join(BASE_DIR, ...) BASE_DIR =

详解shrio的认证(登录)过程

shrio是一个比较轻量级的安全框架,主要的作用是在后端承担认证和授权的工作.今天就讲一下shrio进行认证的一个过程. 首先先介绍一下在认证过程中的几个关键的对象: Subject:主体 访问系统的用户,主体可以是用户.程序等,进行认证的都称为主体: Principal:身份信息 是主体(subject)进行身份认证的标识,标识必须具有唯一性,如用户名.手机号.邮箱地址等,一个主体可以有多个身份,但是必须有一个主身份(Primary Principal). credential:凭证信息 是只

详解vue身份认证管理和租户管理

概述 功能模块的开发往往是最容易的,但是要处理好每个细节就不容易了.就拿这里的身份认证管理模块来说,看似很简单,因为后端接口都是ABP模板里现成的,前端部分无非就是写界面,调接口,绑数据:但是看一下ABP Angular版本的代码,就会发现他其实是有很多细节方面的处理的. 回到vue,因为前端部分的代码文件太多,下面只列出一些需要注意的细节,其他的像vue组件.表格.表单.数据绑定.接口请求之类的其实都差不多就不说了. 按钮级权限 前面章节中实现了菜单权限的控制,按钮权限的道理也是一样的.判断a

详解Laravel服务容器的优势

概述 laravel服务容器就像一个高度自动化的工厂,你需要的东西,定制好模型,使用特定接口来制造. 因为使用了服务容器,laravel中大部分对象实例化的方式是这样的: $obj1 = $container->make('class1', 'class2'); $obj2 = $container->make('class3', 'class4'); 但是在没有使用服务容器的情况下,以下这种方式同样可以做到: $obj1 = new class1(new class2()); $obj2 =

详解springcloud之服务注册与发现

本次分享的是关于springcloud服务注册与发现的内容,将通过分别搭建服务中心,服务注册,服务发现来说明:现在北京这边很多创业公司都开始往springcloud靠了,可能是由于文档和组件比较丰富的原因吧,毕竟是一款目前来说比较完善的微服务架构:本次分享希望能给大家带来好的帮助: Eureka服务中心 Provider注册服务 Consumer发现服务 Eureka服务中心高可用 Eureka服务中心 就我现在了解到并且用的比较多的注册中心有zookeeper和Eureka,我的上上篇文章分享