从0搭建公司SpringCloud Alibaba 微服務鉴权中心服务(详细教程)
阅读本文大概需要 15 分钟。
来自:blog.csdn.net/doomwatcher/article/details/121743887
鉴权中心服务
认识JWT
服务器鉴权完成之后 会生成 json 对象 发送给客户端,之后客户端和服务端传输数据都需要带上这个对象,服务器完全通过这个json对象认定客户端身份,为了防止篡改数据,服务端在生成的时候都会加上签名(加密的意思),服务器不保存session数据也就是无状态,更适合实现扩展 那些环境可以考虑使用jwt呢?用户授权 ,信息交换
JWT组成部分
Header :头部信息
Payload:我们想要传递的数据
Signature :签名
HMACSHA245(base64UrlEncode(header)+"."+base64UrlEncode(payload),secret)
.
号来连接 HHH.PPPP.SSSS
授权,鉴权设计
JWT本质上是通过算法算出的加密字符串,也可以通过算法反向解析出来,他不依赖任何的框架,所以这个功能有可以单独提取出来的前提 我们的电商系统包含多个微服务,很显然我们每个服务都需要鉴权,于是我们把这个方法提取出来,方便复用 高性能鉴权,为什么不在授权中心做鉴权,首先他回头过http请求等一系列操作,我们在本地只用java的话 少去了很多步骤,性能得到倍数的增长
授权编码实现
e-commerce-authority-center
<dependencies>
<!-- spring cloud alibaba nacos discovery 依赖 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>2.2.3.RELEASE</version>
</dependency>
<!-- Java Persistence API, ORM 规范 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- MySQL 驱动, 注意, 这个需要与 MySQL 版本对应 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.12</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.hyc.ecommerce</groupId>
<artifactId>e-commerce-mvc-config</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!-- zipkin = spring-cloud-starter-sleuth + spring-cloud-sleuth-zipkin-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
<version>2.5.0.RELEASE</version>
</dependency>
<!-- screw 生成数据库文档 -->
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.30</version>
</dependency>
<dependency>
<groupId>cn.smallbun.screw</groupId>
<artifactId>screw-core</artifactId>
<version>1.0.3</version>
</dependency>
</dependencies>
配置编写
server:
port: 7000
servlet:
context-path: /ecommerce-authority-center
spring:
application:
name: e-commerce-authority-center
cloud:
nacos:
discovery:
enabled: true # 如果不想使用 Nacos 进行服务注册和发现, 设置为 false 即可
server-addr: 127.0.0.1:8848 # Nacos 服务器地址
# server-addr: 127.0.0.1:8848,127.0.0.1:8849,127.0.0.1:8850 # Nacos 服务器地址
namespace: 1bc13fd5-843b-4ac0-aa55-695c25bc0ac6
metadata:
management:
context-path: ${server.servlet.context-path}/actuator
jpa:
show-sql: true
hibernate:
ddl-auto: none
properties:
hibernate.show_sql: true
hibernate.format_sql: true
open-in-view: false
datasource:
# 数据源
url: jdbc:mysql://127.0.0.1:3306/imooc_e_commerce?autoReconnect=true&useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=UTC
username: root
password: root
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
# 连接池
hikari:
maximum-pool-size: 8
minimum-idle: 4
idle-timeout: 30000
connection-timeout: 30000
max-lifetime: 45000
auto-commit: true
pool-name: ImoocEcommerceHikariCP
kafka:
bootstrap-servers: 127.0.0.1:9092
producer:
retries: 3
consumer:
auto-offset-reset: latest
zipkin:
sender:
type: kafka # 默认是 web
base-url: http://127.0.0.1:9411/
# 暴露端点
management:
endpoints:
web:
exposure:
include: '*'
endpoint:
health:
show-details: always
@EnableJpaAuditing
因为我们用到 自动加入创建时间和修改时间,所以我们需要打开 jpa的自动审计功能,不然会报错@EnableJpaAuditing //允许 jpa 的自动审计
@SpringBootApplication
@EnableDiscoveryClient
public class AuthorityApplication {
public static void main(String[] args) {
SpringApplication.run(AuthorityApplication.class, args);
}
}
/**
* 授权中心测试入口
* 验证授权中心 环境可用性
*/
@SpringBootTest
@RunWith(SpringRunner.class)
public class AuthorityCenterApplicationTest {
@Test
public void conetextLoad() {
}
}
/*
* 用户表实体类定义
* */
@Entity
@EntityListeners(AuditingEntityListener.class)
@Table(name = "t_ecommerce_user")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class EcommerceUser {
/* 自增组件*/
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", nullable = false)
private long id;
/*用户名*/
@Column(name = "username", nullable = false)
private String username;
/* MD5 密码*/
@Column(name = "password", nullable = false)
private String password;
/*额外的信息 json 字符串存储*/
@Column(name = "extra_info", nullable = false)
private String extraInfo;
/*自动加入创建时间 需要主启动类的注解*/
@CreatedDate
@Column(name = "create_time", nullable = false)
private Date createTime;
/*自动加入更新时间 需要主启动类的注解*/
@CreatedDate
@Column(name = "update_time", nullable = false)
private Date updateTime;
}
/**
* EcommerceUserDao 接口定义
*/
public interface EcommerceUserDao extends JpaRepository<EcommerceUser, Long> {
/*
* 根据用户名查询 EcommerceUser 对象
* 等于 select * form t_ecommerce_user where username=?
* */
EcommerceUser findByUsername(String name);
/*
* 根据用户名查询 EcommerceUser 对象
* 等于 select * form t_ecommerce_user where username=? and password=?
* */
EcommerceUser findByUsernameAndPassword(String name, String password);
}
/**
* @author : 冷环渊
* @date : 2021/12/4
* @context: EcommerceUser 相关测试
* @params : null
* @return : * @return : null
*/
@SpringBootTest
@RunWith(SpringRunner.class)
@Slf4j
public class EcommerUserTest {
@Autowired
EcommerceUserDao ecommerceUserDao;
/*测试 新增一个用户数据 */
@Test
public void createUserRecord() {
EcommerceUser ecommerceUser = new EcommerceUser();
//设置要插入的信息
ecommerceUser.setUsername("hyc@qq.com");
ecommerceUser.setPassword(MD5.create().digestHex("123456"));
ecommerceUser.setExtraInfo("{}");
//日志打印返回结果
log.info("server user:[{}]", JSON.toJSON(ecommerceUserDao.save(ecommerceUser)));
}
/*测试 我们编写的自定义方法 查询 刚才创建的新角色*/
@Test
public void SelectUserInfo() {
String username = "hyc@qq.com";
log.info("select userinof:[{}]", JSON.toJSON(ecommerceUserDao.findByUsername(username)));
}
}
生成RSA256的公钥 和 私钥 非对称加密算法
编码
@Slf4j
@SpringBootTest
@RunWith(SpringRunner.class)
/**
*
* @author : 冷环渊
* @date : 2021/12/5
* @context: RSA 非对称 加密算法
* @params : null
* @return : * @return : null
*/
public class RSATest {
@Test
public void generateKeyBytes() throws Exception {
/*获取到 RSA算法实例*/
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
/* 这里最小是 2048 低于的话 是会报错的*/
keyPairGenerator.initialize(2048);
/*
* 生成公钥对
* */
KeyPair keyPair = keyPairGenerator.generateKeyPair();
/*获取 公钥和私钥对象*/
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
log.info("private key:[{}]", Base64.encode(privateKey.getEncoded()));
log.info("public key:[{}]", Base64.encode(publicKey.getEncoded()));
}
}
创建VO对象保存 我们常用且不会变化的值和对象
/**
* @author : 冷环渊
* @date : 2021/12/5
* @context: 鉴权的常量
* @params : null
* @return : * @return : null
*/
public class AuthorCanstant {
/*私钥 只暴露给 鉴权中心 不暴露给任何的其他服务*/
public static final String PRIVATE_KEY = "MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBA" +
"QCMXrQCudalKHJlH16YHr9mI5/xyYnkp5u2gAbMFf2xAHAyykYmixJP3CqG2a8tUwiJjjTIJXP+79Jzgjgg" +
"VbBaTakrvjeFXz9HNP1D4XD6Li+sRVjnN1iBUwIFRxiFN2EOJflA9bqeQLAge/LgAu06y3jdLLleJF7yDRuMH" +
"YedqPl9AJa5RdJmt0OgCoVOqacB7oGkFCFISm0Cwjfgq06nyiiULGZNVt8uhDxZAE4Pi2lmf3yggXCBH9AtU/2" +
"XdyxU9caQJOAbYGxd/mART/NivBjSqo60wcBnktI+booUbDKRBbWRxvfYqKWEwPOwxlJUB3l3pcLZm866Xl3qtVM" +
"XAgMBAAECggEADCGjLRkik+OK/3JWmo8Nu6YYjKz+XeSecIdgDwNXiZSgHcOdjHc4fe5pPn5RxXkHo9vGdAXIoJ/Z" +
"cGIwt5qwQx2zITSvV7eDoIPT36n8OaMEO79Cj7kYzRR/eDVMyTagDLj7ccHK/yJYFnaf5vxZxFsRdwwGeTxreD" +
"/pwZJLxjRSz1W57v5yUJNPPimNB229EogNYHIhQ8+Z7OGiilbtBIL9r6lqlz2hUAVBzXl4kOXFVI+vEodLuV2" +
"rtQXXrpO1+AgH5lZJ7ahShKbqHt/Q6uJSTKAhbsfv/iadcPjmYp2F7nnYBLf66Jln6AWUwnXrJ7XETOf/+Qcib" +
"q/5m6RjAQKBgQDruxn+kaDr5uYQMVSHog+CBRBJghJ4JklhY7ZDYJ2wN2KNHOd3mW/wUVDihVIyRFniIzsWU" +
"0lnI+4OLqNLAZOBaQB5VrjyH4fxn5b26t0xLO1d5EWcOYI8ZRhwWDWaZipe2dUMeqVVMYFeDdTdNsyGrf8x" +
"L+OVyRDiH4s4pBIs7QKBgQCYcIVFgDbrmwsP7lA9/dU9kClutY3gjEUgB2IJp2Y8S4Xhfi4NC8GqRQoMUyuqg" +
"vPHKEiTCa1EojGHS/+r4JVcSg9Wsv64SpGZ+gANxRhfYFPrbkjU4YOMaZeCGUfKR2QnD20c3I4gdQ9kU5nK52n+Y" +
"JEkAFUejg1Mhb6Fp6HDkwKBgAHYYBa3CxxtnUVpLXE2Woq5AWyh4QUhv5dMkYOrgPB9Ln9OR52PDOpDqK9tP" +
"bx4/n8fqXm+QyfUhyuDP/H5XC86JC/O9vmmN4kzp5ndMsgMwvrmK4lShet1GyDd/+VqgVBmwh0r5JlrHske" +
"sJjesfEn8YRwDIcCoOg0OQHDfwTtAoGAQfE61YvXNihFqsiOkaKCYjVAlxGWpDJJnMdU05REl4ScD6WDy" +
"kTxq/RdmmNIGmS3i8mTS3f+Khh3kG2B1ho6wkePRxP7OEGZpqAM8ef22RtUch2tB9neDBmJXtAMzCYB3xu/O" +
"aL3IHdDB0Va2/krUsz3PDmgmK0ed6HLfwm64l0CgYB+iGkMAQEwqYmcCEXKK825Q9y/u8PE9y8uaMGfsZQzDo6v" +
"V5v+reOhmZRrk5BnX+pgztbE28sS6c2vYR0RYoR90aD2GXungCPXWEMDQudHFxvSsNTCYkDynjTSlnzu9aDcfqw1" +
"UIzHog2zCquSro7tnbOMsvV5UdsLBq+WNQGgAw==";
/*默认的 token 超时时间,一天*/
public static final Integer DEFAULT_EXPIRE_DAY = 1;
}
保存 公钥到公用包 以后我们的服务 需要做授权都需要使用到
/**
* @author : 冷环渊
* @date : 2021/12/5
* @context: 通用模块的常量定义
* @params : null
* @return : * @return : null
*/
public class CommonCanstant {
/* RSA 公钥*/
public static final String PUBLIC_KEY = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAjF60ArnWpShyZ" +
"R9emB6/ZiOf8cmJ5KebtoAGzBX9sQBwMspGJosST9wqhtmvLVMIiY40yCVz/u/Sc4I4IFWwWk2pK743hV8/RzT9Q+F" +
"w+i4vrEVY5zdYgVMCBUcYhTdhDiX5QPW6nkCwIHvy4ALtOst43Sy5XiRe8g0bjB2Hnaj5fQCWuUXSZrdDoAqFTqmnA" +
"e6BpBQhSEptAsI34KtOp8oolCxmTVbfLoQ8WQBOD4tpZn98oIFwgR/QLVP9l3csVPXGkCTgG2BsXf5gEU/zYrwY0qqO" +
"tMHAZ5LSPm6KFGwykQW1kcb32KilhMDzsMZSVAd5d6XC2ZvOul5d6rVTFwIDAQAB";
/* JWT 中 存储用户信息到 key*/
public static final String JWT_USER_INFO_KEY = "e-commerce-user";
/*授权中心的 service-id*/
public static final String AUTHORITY_CENTER_SERVICE_ID = "e-commerce-authity-center";
}
用户信息的常用VO对象
/**
* @author : 冷环渊
* @date : 2021/12/5
* @context: 授权中心 鉴权 之后给客户端的token
* @params : null
* @return : * @return : null
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class JwtToken {
/* JWT*/
private String token;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
public class LoginUserinfo {
/*用户 id*/
private Long id;
/*用户名*/
private String username;
}
/**
* @author : 冷环渊
* @date : 2021/12/5
* @context:用户名和密码
* @params : null
* @return : * @return : null
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UsernameAndPassword {
/*用户名 */
private String username;
/*密码*/
private String password;
}
授权服务编写
/**
* @author : 冷环渊
* @date : 2021/12/5
* @context: JWT 相关服务接口定义
* @params : null
* @return : * @return : null
*/
public interface IJWTService {
/*
* 生成 token 使用默认的超时时间
* */
String generateToken(String username, String password) throws Exception;
/*
* 生成 JWT Token 可以设置超时时间 单位是天
* */
String generateToken(String username, String password, Integer expireTime) throws Exception;
/*
* 注册用户并且生成 token 返回
* */
String registerUserAndGenerateToken(UsernameAndPassword usernameAndPassword) throws Exception;
}
授权方法实现类
默认超时时间的 生成 token 自定义超时时间的设置生成token 注册新用户并且生成的token返回
Jwts.builder()
//这里 claim 其实就是 jwt 的 payload 对象 --> KV
.claim(CommonCanstant.JWT_USER_INFO_KEY, JSON.toJSONString(loginUserinfo))
// jwt id 表示是 jwt的id
.setId(UUID.randomUUID().toString())
//jwt 的过期时间
.setExpiration(expireDate)
// 这里是设置加密的私钥和加密类型
.signWith(getPrivateKey(), SignatureAlgorithm.RS256)
//生成 jwt信息 返回的是一个字符串类型
.compact();
}
完整代码
@Service
@Slf4j
@Transactional(rollbackFor = Exception.class)
public class IJWTServiceIpml implements IJWTService {
@Autowired
private EcommerceUserDao ecommerceUserDao;
@Override
public String generateToken(String username, String password) throws Exception {
return generateToken(username, password, 0);
}
@Override
public String generateToken(String username, String password, Integer expireTime) throws Exception {
//首先需要验证用户是否通过授权校验,即 输入的用户名和密码能否寻找到匹配数据表的记录
EcommerceUser ecommerceUser = ecommerceUserDao.findByUsernameAndPassword(username, password);
if (ecommerceUser == null) {
log.error("can not find user:[{}],[{}]", username, password);
return null;
}
//Token 中塞入对象, 即 JWT中 储存的对象,后端拿到这些信息 就可以知道那个用户在操作
LoginUserinfo loginUserinfo = new LoginUserinfo(
ecommerceUser.getId(), ecommerceUser.getUsername()
);
if (expireTime <= 0) {
expireTime = AuthorCanstant.DEFAULT_EXPIRE_DAY;
}
//计算超时时间
ZonedDateTime zdt = LocalDate.now().plus(expireTime, ChronoUnit.DAYS)
.atStartOfDay(ZoneId.systemDefault());
Date expireDate = Date.from(zdt.toInstant());
return Jwts.builder()
//这里 claim 其实就是 jwt 的 payload 对象 --> KV
.claim(CommonCanstant.JWT_USER_INFO_KEY, JSON.toJSONString(loginUserinfo))
// jwt id 表示是 jwt的id
.setId(UUID.randomUUID().toString())
//jwt 的过期时间
.setExpiration(expireDate)
// 这里是设置加密的私钥和加密类型
.signWith(getPrivateKey(), SignatureAlgorithm.RS256)
//生成 jwt信息 返回的是一个字符串类型
.compact();
}
@Override
public String registerUserAndGenerateToken(UsernameAndPassword usernameAndPassword) throws Exception {
//先去校验 用户名是否存在 如果存在 不能重复注册
EcommerceUser oldUser = ecommerceUserDao.findByUsername(usernameAndPassword.getUsername());
if (null != oldUser) {
log.error("username is registered:[{}]", oldUser.getUsername());
return null;
}
EcommerceUser ecommerceUser = new EcommerceUser();
ecommerceUser.setUsername(usernameAndPassword.getUsername());
ecommerceUser.setPassword(usernameAndPassword.getPassword()); //MD5 编码以后
ecommerceUser.setExtraInfo("{}");
//注册一个新用户 写到一个 记录表中
ecommerceUser = ecommerceUserDao.save(ecommerceUser);
log.info("regiter user success:[{}],[{}]", ecommerceUser.getUsername());
//生成 token 并且返回
return generateToken(ecommerceUser.getUsername(), ecommerceUser.getPassword());
}
/*
* 根据本地储存的私钥获取到 PrivateKey对象
* */
private PrivateKey getPrivateKey() throws Exception {
//使用给定的编码密钥创建一个新的PKCS8EncodedKeySpec。
PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(new BASE64Decoder().decodeBuffer(AuthorCanstant.PRIVATE_KEY));
// 设置生成新密钥的工厂加密方式
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
//返回生成好的密钥
return keyFactory.generatePrivate(priPKCS8);
}
}
Controller
@Slf4j
@RestController
@RequestMapping("/authority")
public class AuthorityConroller {
private final IJWTService ljwtService;
public AuthorityConroller(IJWTService ljwtService) {
this.ljwtService = ljwtService;
}
/*
* 从授权中心 获取 token (其实就是登陆功能) 且返回信息中没有统一响应的包装
* */
@IgnoreResponseAdvice
@PostMapping("/token")
public JwtToken token(@RequestBody UsernameAndPassword usernameAndPassword) throws Exception {
//通常 日志里不会答打印用户的信息 防止泄露,我们这本身就是一个授权服务器,本身就不对外开放,所以我们可以打印用户信息到日志方便查看
log.info("request to get token with param:[{}]", JSON.toJSONString(usernameAndPassword));
return new JwtToken(ljwtService.generateToken(
usernameAndPassword.getUsername(),
usernameAndPassword.getPassword()));
}
/*注册用户并且返回注册当前用户的token 就是通过授权中心常见用户*/
@IgnoreResponseAdvice
@PostMapping("/register")
public JwtToken register(@RequestBody UsernameAndPassword usernameAndPassword) throws Exception {
log.info("register user with param:[{}]", JSON.toJSONString(usernameAndPassword));
return new JwtToken(ljwtService.registerUserAndGenerateToken(usernameAndPassword));
}
}
鉴权编码实现
/**
* @author : 冷环渊
* @date : 2021/12/5
* @context: JWT Token 解析工具类
* @params : null
* @return : * @return : null
*/
public class TokenParseUtil {
public static LoginUserinfo parseUserInfoFromToken(String token) throws Exception {
if (null == token) {
return null;
}
Jws<Claims> claimsJws = parseToken(token, getPublicKey());
Claims body = claimsJws.getBody();
//如果 Token 已经过期返回 null
if (body.getExpiration().before(Calendar.getInstance().getTime())) {
return null;
}
// 返回 Token中保存的用户信息
return JSON.parseObject(
body.get(CommonCanstant.JWT_USER_INFO_KEY).toString(), LoginUserinfo.class
);
}
/*
* 通过公钥去解析 JWT Token
* */
private static Jws<Claims> parseToken(String token, PublicKey publicKey) {
// 用设置签名公钥,解析claims信息 token
return Jwts.parser().setSigningKey(publicKey).parseClaimsJws(token);
}
/*
* 根据本地存储的公钥获取到 getPublicKey
* */
public static PublicKey getPublicKey() throws Exception {
//解码器 我们设置解码器 将公钥放进去
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(
new BASE64Decoder().decodeBuffer(CommonCanstant.PUBLIC_KEY)
);
//创建 RSA 实例 通过示例生成公钥对象
return KeyFactory.getInstance("RSA").generatePublic(keySpec);
}
}
验证鉴权授权
/**
* @author : 冷环渊
* @date : 2021/12/5
* @context: JWT 相关测试类
* @params : null
* @return : * @return : null
*/
@Slf4j
@SpringBootTest
@RunWith(SpringRunner.class)
public class JWTServiceTest {
@Autowired
private IJWTService ijwtService;
@Test
public void testGenerateAndParseToken() throws Exception {
String jwtToken = ijwtService.generateToken(
"hyc@qq.com", "e10adc3949ba59abbe56e057f20f883e"
);
log.info("jwt token is:[{}]", jwtToken);
LoginUserinfo userinfo = TokenParseUtil.parseUserInfoFromToken(jwtToken);
log.info("userinfo by jwt prase token :[{}]", JSON.toJSONString(userinfo));
}
}
eyJhbGciOiJSUzI1NiJ9
.eyJlLWNvbW1lcmNlLXVzZXIiOiJ7XCJpZFwiOjExLFwidXNlcm5hbWVcIjpcImh5Y0BxcS5jb21cIn0iLCJqdGkiOiIzNDgwNjdjMi00MTBlLTQ3MjItYmM3ZS02NWQyYmNmYTRkN2MiLCJleHAiOjE2Mzg3MjAwMDB9
.ZbFl81MkIipJSULZLf4F2X2Fb0q1TwhHIMT7nyZsZVwUxXyZnK54RlzoGM_b-kMUdKO_Tab-qEeOT6Jn--FiKmbOziWXiBx3a-k5ipthMJx0Fez-X8Acty-Pg7zukNalugiLxGb5ophQoVQWRTDmv2hytGHqiV71HVyErznkJa36QQr6QsjXqlJleo3BBt-6BFzdTFPLUmdTEJ4XsmZBa_acUDGBhY0_tU2gYtKBWhwvMCknuyCcV-_GVI5EvgMIKRpeFSZrWfTsDG2y1MFcyzjKE6jnzek-YwT3XkzQ8eGzUbiOlaU_Zx5OJah-UtrKwqlAw9WbO71pNgEBefdsYw
userinfo by jwt prase token :[{"id":11,"username":"hyc@qq.com"}]
验证对外提供的接口是否好用
Token 方法
### 获取 Token -- 登录功能实现
POST http://127.0.0.1:7000/ecommerce-authority-center/authority/token
Content-Type: application/json
{
"username": "hyc@qq.com",
"password": "e10adc3949ba59abbe56e057f20f883e"
}
POST http://127.0.0.1:7000/ecommerce-authority-center/authority/token
HTTP/1.1 200
Content-Type: application/json
Transfer-Encoding: chunked
Date: Sun, 05 Dec 2021 15:35:52 GMT
Keep-Alive: timeout=60
Connection: keep-alive
{
"token": "eyJhbGciOiJSUzI1NiJ9.eyJlLWNvbW1lcmNlLXVzZXIiOiJ7XCJpZFwiOjExLFwidXNlcm5hbWVcIjpcImh5Y0BxcS5jb21cIn0iLCJqdGkiOiIxNDU1M2FjZi1lZmE5LTQ4OTgtOTliYS1hNzA4NWI4MjU4MzAiLCJleHAiOjE2Mzg3MjAwMDB9.AlOpo6uf97R20ZLojXeun-3MK8DpSYlWxEygvDrtQeWaM9R0iKx-iW1VXnK6WoEntvqPxIrmPA7khjl3dXPa8kQHtdq-LVO7BDuZZDiQyZ64ZS7A9jWZr5JReSWBUSR1YUnsOvBRMkx4JVcAF3_W7nHwd722FFzOZRCr72hLHQIKpsugKtqjMEtaiEW0vcqphCYRJTAO_rQx1Lb1eVVg_Ufur0qSlKkV5dSJ0x3x9mc9UZRckwN0rrP7wQxZcrxJvKTfX7CkRRSO-CxZbG4WLokSaMtaGBMWU-7KGq7HSCZ0yuOgbbLdouHncsp6VD2tNLFdWSdJ_whCIbZxfX8R7w"
}
验证如果记录数据表没有是否会返回null
### 获取 Token -- 登录功能实现
POST http://127.0.0.1:7000/ecommerce-authority-center/authority/token
Content-Type: application/json
### 随便写的id
{
"username": "hyc1111@qq.com",
"password": "e10adc3949ba59abbe56e057f20f883e"
}
POST http://127.0.0.1:7000/ecommerce-authority-center/authority/token
HTTP/1.1 200
Content-Type: application/json
Transfer-Encoding: chunked
Date: Sun, 05 Dec 2021 15:40:44 GMT
Keep-Alive: timeout=60
Connection: keep-alive
{
"token": null
}
register
### 注册用户并返回 Token -- 注册功能实现
POST http://127.0.0.1:7000/ecommerce-authority-center/authority/register
Content-Type: application/json
{
"username": "hyc@qq.com",
"password": "e10adc3949ba59abbe56e057f20f883e"
}
POST http://127.0.0.1:7000/ecommerce-authority-center/authority/register
HTTP/1.1 200
Content-Type: application/json
Transfer-Encoding: chunked
Date: Sun, 05 Dec 2021 15:42:00 GMT
Keep-Alive: timeout=60
Connection: keep-alive
{
"token": null
}
现在我们去注册一个新的用户
### 注册用户并返回 Token -- 注册功能实现
POST http://127.0.0.1:7000/ecommerce-authority-center/authority/register
Content-Type: application/json
{
"username": "hyc11@qq.com",
"password": "e10adc3949ba59abbe56e057f20f883e"
}
POST http://127.0.0.1:7000/ecommerce-authority-center/authority/register
HTTP/1.1 200
Content-Type: application/json
Transfer-Encoding: chunked
Date: Sun, 05 Dec 2021 15:42:57 GMT
Keep-Alive: timeout=60
Connection: keep-alive
{
"token": "eyJhbGciOiJSUzI1NiJ9.eyJlLWNvbW1lcmNlLXVzZXIiOiJ7XCJpZFwiOjEyLFwidXNlcm5hbWVcIjpcImh5YzExQHFxLmNvbVwifSIsImp0aSI6IjMxNDc0NmIwLTMyOGYtNDZkNS05ZTIwLTg3YjI0OWY1ZjZkOCIsImV4cCI6MTYzODcyMDAwMH0.MKxk-Q4BG5kaYFAsLiy13trtk_gDFmCKORpdE4EAwgSVecXFQcYfT1VvqSAKvoQLFsSlQAxOR5elV8CFOoKwAomwqdyyghZp63NKJ2smRbg3Y-4jWBzFVsUgcjOY2fwh7oNTdHEsWmLBYAh5r0hm_MysZsUEsE-cwb3sw8NSMk1OZp0J6tcRras7V1Uw5xXH8OnCoq2cUfdynJMHS29EzJT1TFPb8unVQ_A1RWodsHdK3n1Bl4wFbJjMtnHx7vzOeAUSNJx1XpAGdo0xYHK6HBpS9E1KBS3x1AnYFONM0DKd4-_QxMkBW1kkg2uWrRpf3GYZF20FKxXgmBAPHGZhew"
}
鉴权服务中心总结
传统:
最为传统的做法,客户端储存 cookie 一般是 Session id 服务器存储 Session Session 是每次用户认证通过以后 ,服务器需要创建一条记录保存用户信息,通常是在内存中(也可以放在redis中),随着认证通过的用户越来越多,服务器的在这里的开销就会越来越大 不同域名之前切换的时候,请求可能会被禁止,即跨越问题
基于token:
JWT与Session的差异相同点是,他们都是存储用户信息。然而Session是在服务器端的,而JWT是在客户端的 JWT方式将用户状态分散到了客户端中,可以明显减轻请服务器的内存压力,服务端只需要用算法解析客户端的token就可以得到信息
两者优缺点的对比
解析方法:JWT使用算法直接解析得到用户信息;Session需要额外的数据映射。实现匹配
管理方法:JWT只有过期时间的限制,Session 数据保存在服务器,可控性更强
跨平台:JWT就是一段字符串,可以任意传播,Session跨平台需要有统一的解析平台,较为繁琐
时效性:JWT一旦生成 独立存在,很难做到特殊的控制;Session时效性完全由服务端的逻辑说了算
推荐阅读:
互联网初中高级大厂面试题(9个G) 内容包含Java基础、JavaWeb、MySQL性能优化、JVM、锁、百万并发、消息队列、高性能缓存、反射、Spring全家桶原理、微服务、Zookeeper......等技术栈!
⬇戳阅读原文领取! 朕已阅
评论