[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

package com.czxy.utils;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
/**
* @author 庭前云落
* @Date 2019/12/13 22:01
* @description
*/
public class RasUtils { /**
* 从文件中读取公钥
*
* @param filename 公钥保存路径,相对于classpath
* @return 公钥对象
* @throws Exception
*/
public static PublicKey getPublicKey(String filename) throws Exception {
byte[] bytes = readFile(filename);
return getPublicKey(bytes);
} /**
* 从文件中读取密钥
*
* @param filename 私钥保存路径,相对于classpath
* @return 私钥对象
* @throws Exception
*/
public static PrivateKey getPrivateKey(String filename) throws Exception {
byte[] bytes = readFile(filename);
return getPrivateKey(bytes);
} /**
* 获取公钥
*
* @param bytes 公钥的字节形式
* @return
* @throws Exception
*/
public static PublicKey getPublicKey(byte[] bytes) throws Exception {
X509EncodedKeySpec spec = new X509EncodedKeySpec(bytes);
KeyFactory factory = KeyFactory.getInstance("RSA");
return factory.generatePublic(spec);
} /**
* 获取密钥
*
* @param bytes 私钥的字节形式
* @return
* @throws Exception
*/
public static PrivateKey getPrivateKey(byte[] bytes) throws Exception {
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(bytes);
KeyFactory factory = KeyFactory.getInstance("RSA");
return factory.generatePrivate(spec);
} /**
* 根据密文,生存rsa公钥和私钥,并写入指定文件
*
* @param publicKeyFilename 公钥文件路径
* @param privateKeyFilename 私钥文件路径
* @param secret 生成密钥的密文
* @throws Exception
*/
public static void generateKey(String publicKeyFilename, String privateKeyFilename, String secret) throws Exception {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
SecureRandom secureRandom = new SecureRandom(secret.getBytes());
keyPairGenerator.initialize(1024, secureRandom);
KeyPair keyPair = keyPairGenerator.genKeyPair();
// 获取公钥并写出
byte[] publicKeyBytes = keyPair.getPublic().getEncoded();
writeFile(publicKeyFilename, publicKeyBytes);
// 获取私钥并写出
byte[] privateKeyBytes = keyPair.getPrivate().getEncoded();
writeFile(privateKeyFilename, privateKeyBytes);
} private static byte[] readFile(String fileName) throws Exception {
return Files.readAllBytes(new File(fileName).toPath());
} private static void writeFile(String destPath, byte[] bytes) throws IOException {
File dest = new File(destPath); //创建父文件夹
if(!dest.getParentFile().exists()){
dest.getParentFile().mkdirs();
}
//创建需要的文件
if (!dest.exists()) {
dest.createNewFile();
} Files.write(dest.toPath(), bytes);
}
}

2.1.1使用工具

//生成公钥和私钥
RasUtils.generateKey(公钥位置,私钥位置,密码);
RasUtils.generateKey(pubKeyPath,priKeyPath,"234");
//获得公钥
RasUtils.getPublicKey(pubKeyPath);
//获得私钥
RasUtils.getPrivateKey(priKeyPath);
package com.czxy;

import com.czxy.utils.RasUtils;
import org.junit.Test; import java.security.PrivateKey;
import java.security.PublicKey; /**
* @author 庭前云落
* @Date 2019/12/13 22:07
* @description
*/
public class TestRAS { private static final String pugbKeyPath="D:\\ras\\ras.pub"; private static final String priKeyPath="D:\\ras\\ras.pri"; @Test
public void testRas() throws Exception {
//生产公钥和私钥
RasUtils.generateKey(pugbKeyPath,priKeyPath,"234");
} @Test
public void testGetRas() throws Exception {
//获得公钥和私钥
PublicKey publicKey = RasUtils.getPublicKey(pugbKeyPath);
PrivateKey privateKey = RasUtils.getPrivateKey(priKeyPath);
System.out.println(publicKey.toString());
System.out.println(privateKey.toString()); }
}

3、JWT工具

3.1概述

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

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

3.2、使用JWT

pom

    <properties>
<jwt.jjwt.version>0.9.0</jwt.jjwt.version>
<jwt.joda.version>2.9.7</jwt.joda.version>
<lombok.version>1.16.20</lombok.version>
<beanutils.version>1.9.3</beanutils.version>
</properties> <dependencies>
<!--网关依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
<!--添加eureka客户端-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!--测试-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<!--jwt依赖-->
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>${beanutils.version}</version>
</dependency> <dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>${jwt.jjwt.version}</version>
</dependency> <dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>${jwt.joda.version}</version>
</dependency> <dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<scope>provided</scope>
</dependency> </dependencies>

导入工具类

工具类:JwtUtils

package com.czxy.utils;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.apache.commons.beanutils.BeanUtils;
import org.joda.time.DateTime; import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.security.PrivateKey;
import java.security.PublicKey; /**
* @author 庭前云落
* @Date 2019/12/13 22:01
* @description
*/
public class JwtUtils {
/**
* 私钥加密token
* @param data 需要加密的数据(载荷内容)
* @param expireMinutes 过期时间,单位:分钟
* @param privateKey 私钥
* @return
*/
public static String generateToken(Object data, int expireMinutes, PrivateKey privateKey) throws Exception {
//1 获得jwt构建对象
JwtBuilder jwtBuilder = Jwts.builder();
//2 设置数据
if( data == null ) {
throw new RuntimeException("数据不能为空");
}
BeanInfo beanInfo = Introspector.getBeanInfo(data.getClass());
PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
// 获得属性名
String name = propertyDescriptor.getName();
// 获得属性值
Object value = propertyDescriptor.getReadMethod().invoke(data);
if(value != null) {
jwtBuilder.claim(name,value);
}
}
//3 设置过期时间
jwtBuilder.setExpiration(DateTime.now().plusMinutes(expireMinutes).toDate());
//4 设置加密
jwtBuilder.signWith(SignatureAlgorithm.RS256, privateKey);
//5 构建
return jwtBuilder.compact();
} /**
* 通过公钥解析token
* @param token 需要解析的数据
* @param publicKey 公钥
* @param beanClass 封装的JavaBean
* @return
* @throws Exception
*/
public static <T> T getObjectFromToken(String token, PublicKey publicKey,Class<T> beanClass) throws Exception {
//1 获得解析后内容
Claims body = Jwts.parser().setSigningKey(publicKey).parseClaimsJws(token).getBody();
//2 将内容封装到对象JavaBean
T bean = beanClass.newInstance();
BeanInfo beanInfo = Introspector.getBeanInfo(beanClass);
PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
// 获得属性名
String name = propertyDescriptor.getName();
// 通过属性名,获得对应解析的数据
Object value = body.get(name);
if(value != null) {
// 将获得的数据封装到对应的JavaBean中
BeanUtils.setProperty(bean,name,value);
}
}
return bean;
}
}

时间处理工具:DateTime

//当前时间
DateTime.now().toDate().toLocaleString()
//当前时间加5分钟
DateTime.now().plusMinutes(5).toDate().toLocaleString()
//当前时间减5分钟
DateTime.now().minusMinutes(5).toDate().toLocaleString()

3.3、测试

//生成数据, UserInfo --> String(加密)
//JwtUtils.generateToken(数据,过期时间(分钟), 私钥)
String token = JwtUtils.generateToken(userInfo,30, RasUtils.getPrivateKey(priKeyPath)); //解析数据, String(加密) --> UserInfo
// JwtUtils.getObjectFromToken(加密数据, 公钥, 封装对象.class);
UserInfo userInfo = JwtUtils.getObjectFromToken(token, RasUtils.getPublicKey(pubKeyPath), UserInfo.class);
  • 生产Token
package com.czxy;

import com.czxy.utils.RasUtils;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.joda.time.DateTime;
import org.junit.Test; /**
* @author 庭前云落
* @Date 2019/12/13 22:32
* @description
*/
public class TestJWT { private static final String pugbKeyPath="D:\\ras\\ras.pub"; private static final String priKeyPath="D:\\ras\\ras.pri"; @Test
public void testGenerateToken() throws Exception {
String str = Jwts.builder()
.claim("test","庭前云落")
.setExpiration(DateTime.now().plusMinutes(60).toDate())
.signWith(SignatureAlgorithm.RS256, RasUtils.getPrivateKey(priKeyPath))
.compact();
System.out.println(str);
}
}
"庭前云落":Token ---》eyJhbGciOiJSUzI1NiJ9.eyJ0ZXN0Ijoi5bqt5YmN5LqR6JC9IiwiZXhwIjoxNTc2MjkzMTEyfQ.a32GamgbG6F1xC-4NtEJNNLX8mcV6Ycyc2bf7_7wX6_xa4LzimqO5ZH9d4bSii-IixYudSreurJ2Rjq72aXvv3nv_VsZasmODeLkBMLtBGhKDztKW3hNQM7rcRLIxL4PFP48xjosJl48F-hXSgEWqYXuC6Voexlk8W4eonRcGqg

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

4、生产token和校验token

编写测试对象UserInfo

package com.czxy.domain;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor; /**
* @author 庭前云落
* @Date 2019/12/13 21:55
* @description
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserInfo {
private Long id;
private String username;
}

测试

package com.czxy;

import com.czxy.domain.UserInfo;
import com.czxy.utils.JwtUtils;
import com.czxy.utils.RasUtils;
import org.junit.Test; /**
* @author 庭前云落
* @Date 2019/12/13 22:32
* @description
*/
public class TestJWT { private static final String pubKeyPath="D:\\ras\\ras.pub"; private static final String priKeyPath="D:\\ras\\ras.pri"; @Test
public void testToken() throws Exception {
UserInfo userInfo = new UserInfo();
userInfo.setId(10L);
userInfo.setUsername("庭前云落");
String token = JwtUtils.generateToken(userInfo, 30, RasUtils.getPrivateKey(priKeyPath));
System.out.println(token);
} @Test
public void testParserToken() throws Exception {
String token="eyJhbGciOiJSUzI1NiJ9.eyJjbGFzcyI6ImNvbS5jenh5LmRvbWFpbi5Vc2VySW5mbyIsImlkIjoxMCwidXNlcm5hbWUiOiLluq3liY3kupHokL0iLCJleHAiOjE1NzYyOTI1Mzd9.LlyCCBeW4f7fjU3LmE7cA8W7aNB1BXp23Yv9WQJouCRCtoD46GiXQAHn2kezuzuPfp2u5G0OXOIeahHtnvRMSDjtQFJ6s-cZcKNupJPOPK8BzuEnladx0ilcrSr5TeWNxujg-svSz5EJRwWj8KbRKhQluohpAg0VhERjJjD5wTY"; UserInfo userInfo = JwtUtils.getObjectFromToken(token, RasUtils.getPublicKey(pubKeyPath), UserInfo.class);
System.out.println(userInfo);
}
}

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. npm use local module

    情况是这样的, 我一个Angular的项目和一个微信小程序要共用逻辑, 于是我就把它剥离出来一个Node类库, Angular倒是可以使用Reference去引用, 但是使用uniapp创建的微信小程 ...

  2. pytorch1.0实现GAN

    import torch import torch.nn as nn import numpy as np import matplotlib.pyplot as plt # 超参数设置 # Hype ...

  3. Mac中设置Sublime快速在终端中使用命令打开项目

    工作中用Atom比较多,比较喜欢Atom可以直接在终端中直接输入atom .就能直接打开项目的功能,于是搜索得知sublime text也有这样的功能,下面就简单的配置了一下: sudo ln -s ...

  4. TCP,SYN,FIN扫描

    1.TCP扫描相对来说是速度比较慢的一种,为什么会慢呢?因为这种方法在扫描的时候会从本地主机的一个端口向目标主机的一个端口发出一个连接请求报文段,而目标主机在收到这个这个请求报文后: 有回复: 若同意 ...

  5. Python+VSCode+Git【转】

    Python+VSCode+Git 学习总结 - 秦无邪 - 博客园

  6. jdk8新特性--函数式接口的使用

    函数式接口的概念: 函数式接口的格式: 示例: 函数式接口的使用: 简化lambda表达式:

  7. 设计模式--装饰者模式(io流中使用的模式)

    重点: 1.动态扩展对象,替换之前需要继承才能实现的功能. 2.具体工作的,仍然是被包装的对象,(有点锦上添花的意思,顾名思义仅仅起到装饰的作用,主体不变). 对比继承: 1.减少类的数量. 如果使用 ...

  8. windows下安装mysql-8.0.18-winx64

    1.下载安装包 安装包现在地址: https://dev.mysql.com/downloads/mysql/ 2.解压缩至安装目录 解压缩下载之后的zip,我这里使用的安装路径为: C:\Progr ...

  9. 【转载】Intellij IDEA神器居然还有这些小技巧

    概述Intellij IDEA真是越用越觉得它强大,它总是在我们写代码的时候,不时给我们来个小惊喜.出于对Intellij IDEA的喜爱,我决定写一个与其相关的专栏或者系列,把一些好用的Intell ...

  10. ASP.NET Core 2.1 中的 HttpClientFactory (Part 1) HttpClientFactory介绍

    原文:https://www.stevejgordon.co.uk/introduction-to-httpclientfactory-aspnetcore  发表于:2018年1月 ASP.NET ...