鉴权中心服务
认识JWT
json web token 是一个开放的标准 ,它定义了一个种紧凑的,自包含的方式,用于作为json对象在各方之间安全的传输信息
服务器鉴权完成之后 会生成 json 对象 发送给客户端,之后客户端和服务端传输数据都需要带上这个对象,服务器完全通过这个json对象认定客户端身份,为了防止篡改数据,服务端在生成的时候都会加上签名(加密的意思),服务器不保存session数据也就是无状态,更适合实现扩展
那些环境可以考虑使用jwt呢? 用户授权 ,信息交换
JWT组成部分
Header :头部信息
Header 由两部分组成(Token类型,加密算法的名称),并且使用的是base64的编码
Payload: 我们想要传递的数据
Payload KV形式的诗数据 ,这里就是我们想要传递的信息(授权的话就是Token信息)
Signature :签名
Signature 为了得到签名 首先我们得有编码过的Header 编码过的payload 和一个密钥。签名用的算法就是header中指定的那个,之后就会对他们签名
我们需要一个签名公式
HMACSHA245(base64UrlEncode(header)+"."+base64UrlEncode(payload),secret)
产生一个签名,返回一个字符串,返回给客户端,之后客户端每次访问都要带上这个字符串,进行鉴权
JWT使用 . 号来连接 HHH.PPPP.SSSS
授权,鉴权设计
这里我们先不考虑 gateway 网关,后续会搭建,我们的重点放在中间和右边部分
鉴权部分,我们独立实现公共的工具类,为什么?以下三点
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>
导入好依赖之后我们 编写对应的配置,如注册到naocs 加入adminserver的监管,配置数据源等 这里我们使用jpa 来做orm
配置编写
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); } }
test包下就测试环境是否正确
/** * 授权中心测试入口 * 验证授权中心 环境可用性 */ @SpringBootTest @RunWith(SpringRunner.class) public class AuthorityCenterApplicationTest { @Test public void conetextLoad() { } }
环境ok之后
我们去测试 数据库操作是否可用
编写实体类ecommerceUser
/* * 用户表实体类定义 * */ @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; }
有了实体类我们需要有数据操作的实现 于是编写Dao 接口
其实当我们创建接口的时候jpa就已经有了对应的基础增删改查的方法
这里我们实现两个自定义查询方法
/** * 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); }
之后创建 test service
/** * @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的公钥 和 私钥 非对称加密算法
他通过 私钥加密 公钥解密来完成验证,目前很多的鉴权 都是 JWTRSA256的算法来加密鉴权的,如果了解不多,就是用RSA256就可以了