为什么自己实现oauth2.0?
因为spring cloud oauth2.0本身的缺点:
①如果想要控制到访问的每个URl,要进行大量的改造
②特别复杂,本省spring mvc拦截器,加上 网关拦截器,已经有很多拦截器了,但是它本身还有自己拦截器,要分析源码才能了解清楚
③抛错格式和本身自己系统不一致,经常遇到抛错格式不一致,然后又自己去寻找源码里面哪里抛错,并不能统一拦截抛错
④改造成本太大,本身理解oauth2.0的思想并不复杂,所以参考他的源码,自己改造了一套适合自己系统的oauth2.0
1. 首先就是数据库几张表的设计
整个鉴权中心一共五张表:
①权限表:
CREATE TABLE `oauth_access_info` (
`access_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '权限id',
`client_id` varchar(100) DEFAULT NULL COMMENT '客户端id',
`user_id` varchar(100) DEFAULT NULL COMMENT '用户id',
`grant_type` varchar(100) NOT NULL COMMENT '支持多种类型用逗号隔开: authorization_code(鉴权码模式),password(用户 名密码),client_credentials(客户凭证),refresh_token(刷新token模式)',
`scope` int(5) DEFAULT '1' COMMENT '资源拥有范围: 1.全部信任 2.部分角色信任',
`web_redirect_uri` varchar(255) DEFAULT NULL COMMENT '客户端的重定向URI,可为空',
`token_validity` bigint(50) DEFAULT NULL COMMENT 'token失效时间',
`refresh_token_validity` bigint(50) DEFAULT NULL COMMENT '刷新token过期时间',
`code_validity` bigint(50) DEFAULT NULL COMMENT 'code失效时间',
`create_time` datetime DEFAULT NULL COMMENT '数据创建时间',
`update_time` datetime DEFAULT NULL COMMENT '数据修改时间',
PRIMARY KEY (`access_id`)
) ENGINE=InnoDB AUTO_INCREMENT=29 DEFAULT CHARSET=utf8mb4 COMMENT='权限token详情表';
权限表是和用户直接相关联的,所以他有用的id,其次就是权限表支持多种鉴权模式
用户名密码模式,就是拿用户名密码获取token, 客户凭证模式,其实也是校验用户的clientKey,clientSecret
鉴权码模式: 其实是第三方登录场景, 比如: 一个APP支持微信登录,首先用户在APP中申请微信登录,然后跳转到微信登录页面
用户输入完微信用户名和密码并且携带者APP跳转网页,请求微信服务器,微信服务器分配一个code,
然后重定向到那个APP页面,APP页面拿到code,去请求APP后台服务,后台服务拿着code,去请求微信获取token,
然后就可以拿着这个token,获取用户微信数据了
刷新token模式: 为什么刷新token还要归为一种模式,其实他是属于客户端拿着有效期内的token,来刷新拿到时长更久一些token
其实本身他分为两类: oauth模式鉴权和oauth2模式鉴权
资源拥有范围: 完全信任是可以请求资源服务的所有资源,简单来说就是可以请求所有接口, 部分角色信任,就是代表鉴权的时候
他只是可以访问部分资源数据
token,code,refresh_token,各自的时间不一样
②token,code表
CREATE TABLE `oauth_token_code` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`access_id` bigint(20) DEFAULT NULL COMMENT '权限id',
`create_type` int(5) NOT NULL DEFAULT '2' COMMENT '1 oauth2.0模式 2 oauth模式',
`token` text COMMENT 'token值',
`code` text COMMENT 'code值',
`refresh_token` text COMMENT '刷新token',
`create_time` datetime DEFAULT NULL,
`update_time` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=51 DEFAULT CHARSET=utf8mb4;
这里重点解释一下为什么要设置 create_type : 因为有可能一个客户端,可同时进行两种鉴权的,
所以要根据这种来区分他进行的是哪种鉴权
这个表因为经常访问,我现在是把他放在了redis里面
数据结构为 key ,value的形式
首先讲一下怎么确认是唯一token, 因为token生成有两种模式,所以 tokenId = 权限表id+create_type
这里为什么需要权限表id,因为后面进行拿到token获取权限表信息的时候,需要得到这个token对应的权限表id
key的组成为: tokenId_token value就是: 当前生成时间 从权限表得到失效时间进行设置key失效时间
③ 资源表:
CREATE TABLE `oauth_resource_info` (
`resource_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '资源id',
`resource_name` varchar(255) DEFAULT NULL COMMENT '资源名称',
`resource_url` varchar(255) DEFAULT NULL COMMENT '系统中的url路径',
`resource_desc` varchar(255) DEFAULT NULL COMMENT '资源描述',
`status` int(5) DEFAULT '1' COMMENT '状态 1.启用 2.废弃',
`create_time` datetime DEFAULT NULL,
`update_time` datetime DEFAULT NULL,
PRIMARY KEY (`resource_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='资源表';
④资源角色表
CREATE TABLE `oauth_role_resource` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`role_id` bigint(20) DEFAULT NULL COMMENT '角色id',
`resource_id` bigint(20) DEFAULT NULL COMMENT '资源id',
`create_time` datetime DEFAULT NULL,
`update_time` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='角色和资源中间表';
⑤角色表
CREATE TABLE `oauth_access_role` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
`role_name` varchar(20) DEFAULT NULL COMMENT '角色名称',
`role_type` tinyint(2) DEFAULT '-1' COMMENT '角色类型 0:超级管理员 1:运营 2:财务 3:技术 4:BD',
`role_desc` varchar(255) DEFAULT NULL COMMENT '角色描述',
`status` tinyint(2) DEFAULT '1' COMMENT '状态 1:有效 0:无效',
`create_time` timestamp NULL DEFAULT NULL COMMENT '创建时间',
`update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='系统角色表';
从这张图中可以看出,用户表和权限表,角色表有直接关系, 而资源表只能被角色表访问
2.生成token
请求参数: grantType 必传 ; 进行哪种模式鉴权, 然后根据这个值,相应从实例化工厂里面取出对应的实例化service类
这样做的好处就是接口中只有一个获取token方法,但是具体的实现在各个实现类中
①去权限表查询权限范围,并且判断是否有生成这种模式的token权限
②根据用户名和密码去用户服务获取用户基本信息,校验用户密码
③ 获取用户的能访问的资源信息
④ 根据JWT 将用户信息进行编码生成token,并且保存到redis里面
3.校验token
在网关中获取访问接口的token,在redis里面找到对应token,并且没失效, 解析token,获取用户信息资源信息,如果不完全信任,
找到封装信息中的资源信息,和访问URL对比,从而决定放行还是拦截
但是这种方式如果资源信息比较多的话,生成token过大,建议去查询资源角色表,从而决定是否放行