[TOC]

1.1、了解微服务状态

微服务集群中的每个服务,对外提供的都是Rest风格的接口,而Rest风格的一个最重要的规范就是:服务的无状态性。

什么是无状态?

1.服务端不保存任何客户端请求者信息

2.客户端的每次请求必须自备描述信息,通过这些信息识别客户端身份

无状态,在微服务开放中,优势是?

1.客户端请求不依赖服务端的信息,任何多次请求不需要必须访问到同一台服务

2.服务端的是否集群对客户端透明

3.服务端可以任意的迁移和伸缩

4.减小服务端储存压力

1.2、无状态登录实现原理

  • 服务器端生产唯一标识(注意:最终需要进行校验)

    • 方案1:UUID,数据单一,不能包含种类过多的信息。
    • 方案2:RAS加密,数据多样,需要使用算法,有一定的理解难度。【使用】
  • 浏览器储存和自动携带数据
    • 方案1:使用cookie,有很多局限性(大小,个数)
    • 方案2:请求参数,get请求URL有长度限制,每一个路径都需要处理比较麻烦。
    • 方案3:浏览器localStroage存储,请求头携带。【使用】

2.1、RAS工具

服务与服务之间共享数据,采用JWT先生成数据,在另一个服务中解析数据,为了保证数据安全性,使用RAS对数据进行加密。

使用RAS加密保证token数据在传输过程中不会被篡改

  • RAS:非对称加密算法

    • 特点

      • 同时生产一对密钥:公钥和私钥
      • 公钥秘钥:用于加密
      • 私钥秘钥:用于解密

工具类RasUtils

  1. package com.czxy.utils;
  2. import java.io.File;
  3. import java.io.IOException;
  4. import java.nio.file.Files;
  5. import java.security.*;
  6. import java.security.spec.PKCS8EncodedKeySpec;
  7. import java.security.spec.X509EncodedKeySpec;
  8. /**
  9. * @author 庭前云落
  10. * @Date 2019/12/13 22:01
  11. * @description
  12. */
  13. public class RasUtils {
  14. /**
  15. * 从文件中读取公钥
  16. *
  17. * @param filename 公钥保存路径,相对于classpath
  18. * @return 公钥对象
  19. * @throws Exception
  20. */
  21. public static PublicKey getPublicKey(String filename) throws Exception {
  22. byte[] bytes = readFile(filename);
  23. return getPublicKey(bytes);
  24. }
  25. /**
  26. * 从文件中读取密钥
  27. *
  28. * @param filename 私钥保存路径,相对于classpath
  29. * @return 私钥对象
  30. * @throws Exception
  31. */
  32. public static PrivateKey getPrivateKey(String filename) throws Exception {
  33. byte[] bytes = readFile(filename);
  34. return getPrivateKey(bytes);
  35. }
  36. /**
  37. * 获取公钥
  38. *
  39. * @param bytes 公钥的字节形式
  40. * @return
  41. * @throws Exception
  42. */
  43. public static PublicKey getPublicKey(byte[] bytes) throws Exception {
  44. X509EncodedKeySpec spec = new X509EncodedKeySpec(bytes);
  45. KeyFactory factory = KeyFactory.getInstance("RSA");
  46. return factory.generatePublic(spec);
  47. }
  48. /**
  49. * 获取密钥
  50. *
  51. * @param bytes 私钥的字节形式
  52. * @return
  53. * @throws Exception
  54. */
  55. public static PrivateKey getPrivateKey(byte[] bytes) throws Exception {
  56. PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(bytes);
  57. KeyFactory factory = KeyFactory.getInstance("RSA");
  58. return factory.generatePrivate(spec);
  59. }
  60. /**
  61. * 根据密文,生存rsa公钥和私钥,并写入指定文件
  62. *
  63. * @param publicKeyFilename 公钥文件路径
  64. * @param privateKeyFilename 私钥文件路径
  65. * @param secret 生成密钥的密文
  66. * @throws Exception
  67. */
  68. public static void generateKey(String publicKeyFilename, String privateKeyFilename, String secret) throws Exception {
  69. KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
  70. SecureRandom secureRandom = new SecureRandom(secret.getBytes());
  71. keyPairGenerator.initialize(1024, secureRandom);
  72. KeyPair keyPair = keyPairGenerator.genKeyPair();
  73. // 获取公钥并写出
  74. byte[] publicKeyBytes = keyPair.getPublic().getEncoded();
  75. writeFile(publicKeyFilename, publicKeyBytes);
  76. // 获取私钥并写出
  77. byte[] privateKeyBytes = keyPair.getPrivate().getEncoded();
  78. writeFile(privateKeyFilename, privateKeyBytes);
  79. }
  80. private static byte[] readFile(String fileName) throws Exception {
  81. return Files.readAllBytes(new File(fileName).toPath());
  82. }
  83. private static void writeFile(String destPath, byte[] bytes) throws IOException {
  84. File dest = new File(destPath);
  85. //创建父文件夹
  86. if(!dest.getParentFile().exists()){
  87. dest.getParentFile().mkdirs();
  88. }
  89. //创建需要的文件
  90. if (!dest.exists()) {
  91. dest.createNewFile();
  92. }
  93. Files.write(dest.toPath(), bytes);
  94. }
  95. }

2.1.1使用工具

  1. //生成公钥和私钥
  2. RasUtils.generateKey(公钥位置,私钥位置,密码);
  3. RasUtils.generateKey(pubKeyPath,priKeyPath,"234");
  4. //获得公钥
  5. RasUtils.getPublicKey(pubKeyPath);
  6. //获得私钥
  7. RasUtils.getPrivateKey(priKeyPath);
  1. package com.czxy;
  2. import com.czxy.utils.RasUtils;
  3. import org.junit.Test;
  4. import java.security.PrivateKey;
  5. import java.security.PublicKey;
  6. /**
  7. * @author 庭前云落
  8. * @Date 2019/12/13 22:07
  9. * @description
  10. */
  11. public class TestRAS {
  12. private static final String pugbKeyPath="D:\\ras\\ras.pub";
  13. private static final String priKeyPath="D:\\ras\\ras.pri";
  14. @Test
  15. public void testRas() throws Exception {
  16. //生产公钥和私钥
  17. RasUtils.generateKey(pugbKeyPath,priKeyPath,"234");
  18. }
  19. @Test
  20. public void testGetRas() throws Exception {
  21. //获得公钥和私钥
  22. PublicKey publicKey = RasUtils.getPublicKey(pugbKeyPath);
  23. PrivateKey privateKey = RasUtils.getPrivateKey(priKeyPath);
  24. System.out.println(publicKey.toString());
  25. System.out.println(privateKey.toString());
  26. }
  27. }

3、JWT工具

3.1概述

JWT,全称是JSON Web Token,是JSON风格轻量级的授权和身份认证规范,可实现无状态、分布式的Web应用授权:官网:https://jwt.io

  • JWT基于JSON的认证规范。(Json Web Token)
  • 使用JWT目的:生成数据、解析数据

3.2、使用JWT

pom

  1. <properties>
  2. <jwt.jjwt.version>0.9.0</jwt.jjwt.version>
  3. <jwt.joda.version>2.9.7</jwt.joda.version>
  4. <lombok.version>1.16.20</lombok.version>
  5. <beanutils.version>1.9.3</beanutils.version>
  6. </properties>
  7. <dependencies>
  8. <!--网关依赖-->
  9. <dependency>
  10. <groupId>org.springframework.cloud</groupId>
  11. <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
  12. </dependency>
  13. <!--添加eureka客户端-->
  14. <dependency>
  15. <groupId>org.springframework.cloud</groupId>
  16. <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
  17. </dependency>
  18. <!--测试-->
  19. <dependency>
  20. <groupId>org.springframework.boot</groupId>
  21. <artifactId>spring-boot-starter-test</artifactId>
  22. </dependency>
  23. <!--jwt依赖-->
  24. <dependency>
  25. <groupId>commons-beanutils</groupId>
  26. <artifactId>commons-beanutils</artifactId>
  27. <version>${beanutils.version}</version>
  28. </dependency>
  29. <dependency>
  30. <groupId>io.jsonwebtoken</groupId>
  31. <artifactId>jjwt</artifactId>
  32. <version>${jwt.jjwt.version}</version>
  33. </dependency>
  34. <dependency>
  35. <groupId>joda-time</groupId>
  36. <artifactId>joda-time</artifactId>
  37. <version>${jwt.joda.version}</version>
  38. </dependency>
  39. <dependency>
  40. <groupId>org.projectlombok</groupId>
  41. <artifactId>lombok</artifactId>
  42. <version>${lombok.version}</version>
  43. <scope>provided</scope>
  44. </dependency>
  45. </dependencies>

导入工具类

工具类:JwtUtils

  1. package com.czxy.utils;
  2. import io.jsonwebtoken.Claims;
  3. import io.jsonwebtoken.JwtBuilder;
  4. import io.jsonwebtoken.Jwts;
  5. import io.jsonwebtoken.SignatureAlgorithm;
  6. import org.apache.commons.beanutils.BeanUtils;
  7. import org.joda.time.DateTime;
  8. import java.beans.BeanInfo;
  9. import java.beans.Introspector;
  10. import java.beans.PropertyDescriptor;
  11. import java.security.PrivateKey;
  12. import java.security.PublicKey;
  13. /**
  14. * @author 庭前云落
  15. * @Date 2019/12/13 22:01
  16. * @description
  17. */
  18. public class JwtUtils {
  19. /**
  20. * 私钥加密token
  21. * @param data 需要加密的数据(载荷内容)
  22. * @param expireMinutes 过期时间,单位:分钟
  23. * @param privateKey 私钥
  24. * @return
  25. */
  26. public static String generateToken(Object data, int expireMinutes, PrivateKey privateKey) throws Exception {
  27. //1 获得jwt构建对象
  28. JwtBuilder jwtBuilder = Jwts.builder();
  29. //2 设置数据
  30. if( data == null ) {
  31. throw new RuntimeException("数据不能为空");
  32. }
  33. BeanInfo beanInfo = Introspector.getBeanInfo(data.getClass());
  34. PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
  35. for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
  36. // 获得属性名
  37. String name = propertyDescriptor.getName();
  38. // 获得属性值
  39. Object value = propertyDescriptor.getReadMethod().invoke(data);
  40. if(value != null) {
  41. jwtBuilder.claim(name,value);
  42. }
  43. }
  44. //3 设置过期时间
  45. jwtBuilder.setExpiration(DateTime.now().plusMinutes(expireMinutes).toDate());
  46. //4 设置加密
  47. jwtBuilder.signWith(SignatureAlgorithm.RS256, privateKey);
  48. //5 构建
  49. return jwtBuilder.compact();
  50. }
  51. /**
  52. * 通过公钥解析token
  53. * @param token 需要解析的数据
  54. * @param publicKey 公钥
  55. * @param beanClass 封装的JavaBean
  56. * @return
  57. * @throws Exception
  58. */
  59. public static <T> T getObjectFromToken(String token, PublicKey publicKey,Class<T> beanClass) throws Exception {
  60. //1 获得解析后内容
  61. Claims body = Jwts.parser().setSigningKey(publicKey).parseClaimsJws(token).getBody();
  62. //2 将内容封装到对象JavaBean
  63. T bean = beanClass.newInstance();
  64. BeanInfo beanInfo = Introspector.getBeanInfo(beanClass);
  65. PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
  66. for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
  67. // 获得属性名
  68. String name = propertyDescriptor.getName();
  69. // 通过属性名,获得对应解析的数据
  70. Object value = body.get(name);
  71. if(value != null) {
  72. // 将获得的数据封装到对应的JavaBean中
  73. BeanUtils.setProperty(bean,name,value);
  74. }
  75. }
  76. return bean;
  77. }
  78. }

时间处理工具:DateTime

  1. //当前时间
  2. DateTime.now().toDate().toLocaleString()
  3. //当前时间加5分钟
  4. DateTime.now().plusMinutes(5).toDate().toLocaleString()
  5. //当前时间减5分钟
  6. DateTime.now().minusMinutes(5).toDate().toLocaleString()

3.3、测试

  1. //生成数据, UserInfo --> String(加密)
  2. //JwtUtils.generateToken(数据,过期时间(分钟), 私钥)
  3. String token = JwtUtils.generateToken(userInfo,30, RasUtils.getPrivateKey(priKeyPath));
  4. //解析数据, String(加密) --> UserInfo
  5. // JwtUtils.getObjectFromToken(加密数据, 公钥, 封装对象.class);
  6. UserInfo userInfo = JwtUtils.getObjectFromToken(token, RasUtils.getPublicKey(pubKeyPath), UserInfo.class);
  • 生产Token
  1. package com.czxy;
  2. import com.czxy.utils.RasUtils;
  3. import io.jsonwebtoken.Jwts;
  4. import io.jsonwebtoken.SignatureAlgorithm;
  5. import org.joda.time.DateTime;
  6. import org.junit.Test;
  7. /**
  8. * @author 庭前云落
  9. * @Date 2019/12/13 22:32
  10. * @description
  11. */
  12. public class TestJWT {
  13. private static final String pugbKeyPath="D:\\ras\\ras.pub";
  14. private static final String priKeyPath="D:\\ras\\ras.pri";
  15. @Test
  16. public void testGenerateToken() throws Exception {
  17. String str = Jwts.builder()
  18. .claim("test","庭前云落")
  19. .setExpiration(DateTime.now().plusMinutes(60).toDate())
  20. .signWith(SignatureAlgorithm.RS256, RasUtils.getPrivateKey(priKeyPath))
  21. .compact();
  22. System.out.println(str);
  23. }
  24. }
  1. "庭前云落"Token ---》eyJhbGciOiJSUzI1NiJ9.eyJ0ZXN0Ijoi5bqt5YmN5LqR6JC9IiwiZXhwIjoxNTc2MjkzMTEyfQ.a32GamgbG6F1xC-4NtEJNNLX8mcV6Ycyc2bf7_7wX6_xa4LzimqO5ZH9d4bSii-IixYudSreurJ2Rjq72aXvv3nv_VsZasmODeLkBMLtBGhKDztKW3hNQM7rcRLIxL4PFP48xjosJl48F-hXSgEWqYXuC6Voexlk8W4eonRcGqg

  • 解析Token
  1. @Test
  2. public void testParseToken() throws Exception {
  3. String token="\n" +
  4. "eyJhbGciOiJSUzI1NiJ9.eyJ0ZXN0Ijoi5bqt5YmN5LqR6JC9IiwiZXhwIjoxNTc2MjkzMTEyfQ.a32GamgbG6F1xC-4NtEJNNLX8mcV6Ycyc2bf7_7wX6_xa4LzimqO5ZH9d4bSii-IixYudSreurJ2Rjq72aXvv3nv_VsZasmODeLkBMLtBGhKDztKW3hNQM7rcRLIxL4PFP48xjosJl48F-hXSgEWqYXuC6Voexlk8W4eonRcGqg";
  5. Claims claims = Jwts.parser().setSigningKey(RasUtils.getPublicKey(pubKeyPath)).
  6. parseClaimsJws(token).getBody();
  7. String text = claims.get("test",String.class);
  8. System.out.println(text);
  9. }

4、生产token和校验token

编写测试对象UserInfo

  1. package com.czxy.domain;
  2. import lombok.AllArgsConstructor;
  3. import lombok.Data;
  4. import lombok.NoArgsConstructor;
  5. /**
  6. * @author 庭前云落
  7. * @Date 2019/12/13 21:55
  8. * @description
  9. */
  10. @Data
  11. @AllArgsConstructor
  12. @NoArgsConstructor
  13. public class UserInfo {
  14. private Long id;
  15. private String username;
  16. }

测试

  1. package com.czxy;
  2. import com.czxy.domain.UserInfo;
  3. import com.czxy.utils.JwtUtils;
  4. import com.czxy.utils.RasUtils;
  5. import org.junit.Test;
  6. /**
  7. * @author 庭前云落
  8. * @Date 2019/12/13 22:32
  9. * @description
  10. */
  11. public class TestJWT {
  12. private static final String pubKeyPath="D:\\ras\\ras.pub";
  13. private static final String priKeyPath="D:\\ras\\ras.pri";
  14. @Test
  15. public void testToken() throws Exception {
  16. UserInfo userInfo = new UserInfo();
  17. userInfo.setId(10L);
  18. userInfo.setUsername("庭前云落");
  19. String token = JwtUtils.generateToken(userInfo, 30, RasUtils.getPrivateKey(priKeyPath));
  20. System.out.println(token);
  21. }
  22. @Test
  23. public void testParserToken() throws Exception {
  24. String token="eyJhbGciOiJSUzI1NiJ9.eyJjbGFzcyI6ImNvbS5jenh5LmRvbWFpbi5Vc2VySW5mbyIsImlkIjoxMCwidXNlcm5hbWUiOiLluq3liY3kupHokL0iLCJleHAiOjE1NzYyOTI1Mzd9.LlyCCBeW4f7fjU3LmE7cA8W7aNB1BXp23Yv9WQJouCRCtoD46GiXQAHn2kezuzuPfp2u5G0OXOIeahHtnvRMSDjtQFJ6s-cZcKNupJPOPK8BzuEnladx0ilcrSr5TeWNxujg-svSz5EJRwWj8KbRKhQluohpAg0VhERjJjD5wTY";
  25. UserInfo userInfo = JwtUtils.getObjectFromToken(token, RasUtils.getPublicKey(pubKeyPath), UserInfo.class);
  26. System.out.println(userInfo);
  27. }
  28. }

5、JWT token组成

JWT的token包含三部分数据:头部、载荷、签名。

名称 描述 组成部分
头部(Header) 通常头部有两部分信息 1. 声明类型,这里是JW2. 加密算法,自定义
载荷(Payload) 就是有效数据 1. 用户身份信息2. 注册声明
签名(Signature) 整个数据的认证信息 一般根据前两步的数据,再加上服务的的密钥(secret),通过加密算法生成。用于验证整个数据完整和可靠性
  • 生成的数据格式

来源:http://www.1994july.club/seo/

密码加密与微服务鉴权JWT详细使用的更多相关文章

  1. 密码加密与微服务鉴权JWT

    博客学习目标 1.用户注册时候,对数据库中用户的密码进行加密存储(使用 SpringSecurity). 2.使用 JWT 鉴权认证. 一.BCrypt 密码加密 1.常见的加密方式 任何应用考虑到安 ...

  2. JWT对SpringCloud进行系统认证和服务鉴权

    JWT对SpringCloud进行系统认证和服务鉴权 一.为什么要使用jwt?在微服务架构下的服务基本都是无状态的,传统的使用session的方式不再适用,如果使用的话需要做同步session机制,所 ...

  3. spring cloud jwt用户鉴权及服务鉴权

    用户鉴权 客户端请求服务时,根据提交的token获取用户信息,看是否有用户信息及用户信息是否正确 服务鉴权 微服务中,一般有多个服务,服务与服务之间相互调用时,有的服务接口比较敏感,比如资金服务,不允 ...

  4. 畅购商城(八):微服务网关和JWT令牌

    好好学习,天天向上 本文已收录至我的Github仓库DayDayUP:github.com/RobodLee/DayDayUP,欢迎Star,更多文章请前往:目录导航 畅购商城(一):环境搭建 畅购商 ...

  5. Spring Cloud OAuth2.0 微服务中配置 Jwt Token 签名/验证

    关于 Jwt Token 的签名与安全性前面已经做了几篇介绍,在 IdentityServer4 中定义了 Jwt Token 与 Reference Token 两种验证方式(https://www ...

  6. shiro jwt 构建无状态分布式鉴权体系

    一:JWT 1.令牌构造 JWT(json web token)是可在网络上传输的用于声明某种主张的令牌(token),以JSON 对象为载体的轻量级开放标准(RFC 7519). 一个JWT令牌的定 ...

  7. SpringCloud微服务实战——搭建企业级开发框架(二十三):Gateway+OAuth2+JWT实现微服务统一认证授权

      OAuth2是一个关于授权的开放标准,核心思路是通过各类认证手段(具体什么手段OAuth2不关心)认证用户身份,并颁发token(令牌),使得第三方应用可以使用该token(令牌)在限定时间.限定 ...

  8. SpringCloud微服务实战——搭建企业级开发框架(四十四):【微服务监控告警实现方式一】使用Actuator + Spring Boot Admin实现简单的微服务监控告警系统

      业务系统正常运行的稳定性十分重要,作为SpringBoot的四大核心之一,Actuator让你时刻探知SpringBoot服务运行状态信息,是保障系统正常运行必不可少的组件.   spring-b ...

  9. SpringCloud 微服务最佳开发实践

    Maven规范 所有项目必须要有一个统一的parent模块 所有微服务工程都依赖这个parent,parent用于管理依赖版本,maven仓库,jar版本的统一升级维护 在parent下层可以有 co ...

随机推荐

  1. python基础学习(十)

    21.文件操作 # r只读 w只写(原来文件会消失!!!,也可以创建新文件) a追 # 加 r+ 读写 story_file = open("Story.txt", "r ...

  2. CentOS7 搭建 Consul 集群

    环境准备: ssh shell工具: 远程连接 三个CentOS示例: 部署集群 配置好各个实例之间的网络访问,以及ssh免密登录. 下载&上传: 1.下载 Consul: Download ...

  3. Ubuntu 18.04 LTS 设置代理(系统代理;http 代理;sock5 代理;apt 代理 ...)

    1. 设置系统代理 1.1 设置 http 代理 1.1.1 只在当前 shell 生效 export http_proxy="http://<user>:<passwor ...

  4. 高可用etcd集群(三节点) + ssl双向认证

    # etcd下载地址 https://github.com/etcd-io/etcd/tags wget https://github.com/etcd-io/etcd/releases/downlo ...

  5. Python--遍历文件夹下所有文件和目录的方法(os.walk(rootdir)函数返回一个三元素元祖)

    import os import os.path # This folder is custom rootdir = '/Users/macbookpro/Desktop/test' for pare ...

  6. 小程序的目录结构/配置介绍/视图层wxml数据绑定/双线程模型/小程序的启动流程

    安装好微信小程序开发软件,创建项目 小程序文件结构和传统web对比 结构 传统web 微信小程序 结构 HTML WXML 样式 CSS WXSS 逻辑 Javascript Javascript 配 ...

  7. WEB学习路线2019完整版(附视频教程+网盘下载地址)

    WEB学习路线2019完整版(附视频教程+网盘下载地址).适合初学者的最新WEB前端学习路线汇总! 在当下来说web前端开发工程师可谓是高福利.高薪水的职业了.所以现在学习web前端开发的技术人员也是 ...

  8. 每周分享五个 PyCharm 使用技巧(三)

    文章首发于 微信公众号:Python编程时光 PyCharm 是大多数 Python 开发者的首选 IDE,每天我们都在上面敲着熟悉的代码,写出一个又一个奇妙的功能. 一个每天都在使用的工具,如果能掌 ...

  9. Python中闭包的原理

    定义: 如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包(closure). 简单闭包的例子: 下面是一个使用闭包简单的例子,模拟一个计数器,通过将 ...

  10. vue动态请求到的多重数组循环遍历,取值问题,如果某个值存在则显示,不存在则不显示。

    数据结构: 需求:我在vue页面需要拿到url值并显示图片 代码写法: 注意:一定要判断否则拿到的large对象一直是空值, 那么img.large.url将会取不到值,会报 url  'undefi ...