最新版微服务架构鉴权解决方案Spring Cloud Gateway + Oauth2.0+mybatis+mysql+redis+nacos 统一认证和鉴权
目录
鉴权架构图
一、服务列表和技术版本列表
1. 服务列表
2. 技术版本列表
二、Oauth2.0 核心接口和概念简介
1. Oauth2 对外暴露的重要端口(REST API接口)
1) 申请token接口/oauth/token
2) 校验token接口 /oauth/check_token
2. Oauth2 定义的角色
三、环境搭建
1. 启动Nacos注册中心
2. 创建Oauth2 Server 工程并注册到Nacos
数据库设计
oauth_client_details
oauth_access_token
oauth_refresh_token
sys_user
1) pom.xml
2) application.yml
3) 配置授权服务器
4) 配置资源服务器
5) 配置HttpSecurity
6) 集成mybatis基于数据库认证
3. 整合网关gateway
1) 创建一个网关服务实现请求转发到auth-server
2) pom.xml
3) 配置路由
4) 测试网关转发请求
四、将普通微服务配置为资源服务器由网关统一转发认证
1. 创建user-service
2. 配置资源服务器
3. 配置oauth2 client 统一转发请求
五、统一鉴权的四种模式验证
2. 简化模式(implicit)
3. 密码模式(password)
4. 客户端模式(client_credentials)
鉴权架构图
一、服务列表和技术版本列表
1. 服务列表
服务名称 | 说明 |
nacos-server | 服务注册中心,提供服务发现与注册功能。 |
springcloud-gateway | 提供网关服务,网关尽量不做鉴权相关的操作,主要将请求路由到各微服务,实现统一转发请求到授权服务器 |
oauth2-project | 提供授权服务,通过客户端认证模式,将所有从网关转发过来的请求进行认证和授权。 |
user-service | 用户服务,普通微服务,同时也是资源服务器,需要配置鉴权转发。 |
2. 技术版本列表
名称 | 组 | 版本 | 说明 |
Java version | 11 | 为了支持springboot最新版本,使用java 11 | |
spring-boot-starter-parent |
org.springframework.boot |
2.6.3 | Spring boot 版本 |
spring-cloud-dependencies |
org.springframework.cloud |
2021.0.1 | Spring Cloud 版本 |
spring-cloud-alibaba-dependencies |
com.alibaba.cloud |
2021.0.1.0 | Spring Cloud Alibaba 版本 |
spring-cloud-starter-gateway |
org.springframework.cloud |
3.1.1 | Gateway 版本 |
spring-cloud-starter-alibaba-nacos-discovery |
com.alibaba.cloud |
2021.0.1.0 | Nacos服务发现版本 |
spring-cloud-loadbalancer |
org.springframework.cloud |
3.1.1 | 新版本gateway默认不支持 lb转发,因此需要添加此依赖支持与nacos转发的负载均衡 |
spring-cloud-starter-oauth2 |
org.springframework.cloud
|
2.2.4.RELEASE | oauth2 安全框架 |
nacos-server |
2.0.4 | naocs 注册中心 | |
mysql-connector-java |
mysql
|
5.1.46 | 提供mysql驱动和数据源 |
mybatis-spring-boot-starter |
org.mybatis.spring.boot |
2.1.4 | 提供mybatis支持 |
mybatis-plus-boot-starter |
com.baomidou |
3.2.0 | 提供mybatis-plus支持 |
spring-boot-starter-data-redis |
org.springframework.boot |
2.6.3 | 提供redis支持 |
二、Oauth2.0 核心接口和概念简介
oauth2 是一个能由开发者定制的安全框架,我们可以借助oauth2来完成系统应用的授权和认证,从而达到保护应用安全的目的。
1. Oauth2 对外暴露的重要端口(REST API接口)
oauth2 框架中自了几个重要的API,我们用它前一定要熟悉以下的接口。
1) 申请token接口/oauth/token
/oauth/token接口在org.springframework.security.oauth2.provider.endpoint里的TokenEndpoint类里, 该接口会以post请求方式对外提供以表单形式的认证方式,通过了就会返回带期限的token。
通过表单的形式提交, 通过后会返回一个带期限的access_token, 如下用客户端模式去申请token:
2) 校验token接口 /oauth/check_token
/oauth/check_token接口在org.springframework.security.oauth2.provider.endpoint包里的CheckTokenEndpoint类里,请求方式get和post都可以,可以看到需要一个token参数
校验成功后会返回客户端信息:
3) 授权接口/oauth/authorize
/oauth/authorize接口在org.springframework.security.oauth2.provider.endpoint包里的AuthorizationEndpoint, 该接口可用于授权模式的授权操作, 也可用于简化模式的直接申请token。
2. Oauth2 定义的角色
授权服务器: 给具有权限的资源拥有者对应的访问请求授权。
资源服务器: 受保护的资源,可以为静态资源、接口等。
资源拥有者: 具有该系统资源的拥有者,最终受益于用户。
客户端: 与用户和资源、授权服务器沟通的平台,如一个web应用客户端,QQ、微信以第三方形式登录的客户端。
理解了Oauth2的概念后,我们把注册中心、授权服务器、网关搭起来。
三、环境搭建
1. 启动Nacos注册中心
可以参考以下文章启动nacos-server
Nacos源码系列(一) 源码编译_Dream_it_possible!的博客-CSDN博客_nacos 源码编译
2. 创建Oauth2 Server 工程并注册到Nacos
数据库设计
oauth_client_details
oauth2 认证client-id,client-secret,grant_type等信息需要的数据库表。
/*
Navicat Premium Data Transfer
Source Server : win-local
Source Server Type : MySQL
Source Server Version : 50737
Source Host : localhost:3306
Source Schema : oauth2
Target Server Type : MySQL
Target Server Version : 50737
File Encoding : 65001
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for oauth_client_details
-- ----------------------------
DROP TABLE IF EXISTS `oauth_client_details`;
CREATE TABLE `oauth_client_details` (
`client_id` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '客户端ID',
`resource_ids` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '资源ID集合,多个资源时用英文逗号分隔',
`client_secret` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '客户端密匙',
`scope` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '客户端申请的权限范围',
`authorized_grant_types` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '客户端支持的grant_type',
`web_server_redirect_uri` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '重定向URI',
`authorities` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '客户端所拥有的SpringSecurity的权限值,多个用英文逗号分隔',
`access_token_validity` int(11) NULL DEFAULT NULL COMMENT '访问令牌有效时间值(单位秒)',
`refresh_token_validity` int(11) NULL DEFAULT NULL COMMENT '更新令牌有效时间值(单位秒)',
`additional_information` varchar(4096) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '预留字段',
`autoapprove` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '用户是否自动Approval操作',
PRIMARY KEY (`client_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '客户端信息' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of oauth_client_details
-- ----------------------------
INSERT INTO `oauth_client_details` VALUES ('client-app', NULL, '$2a$10$ABFuX9AEpqlGdgJfTrevb.J.5oAx18R7nKOENj13EBtGrYEvkrUoa', 'all', 'password,refresh_token,client_credentials,authorization_code,implicit', 'http://127.0.0.1:9010/dashboard', NULL, 3600, 604800, NULL, '1');
SET FOREIGN_KEY_CHECKS = 1;
oauth_access_token
该表是存放客户端token 信息的表, 生成token时落库,验证时从该表里取出验证。
/*
Navicat Premium Data Transfer
Source Server : win-local
Source Server Type : MySQL
Source Server Version : 50737
Source Host : localhost:3306
Source Schema : oauth2
Target Server Type : MySQL
Target Server Version : 50737
File Encoding : 65001
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for oauth_access_token
-- ----------------------------
DROP TABLE IF EXISTS `oauth_access_token`;
CREATE TABLE `oauth_access_token` (
`token_id` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 'MD5加密的access_token的值',
`token` blob NULL COMMENT 'OAuth2AccessToken.java对象序列化后的二进制数据',
`authentication_id` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT 'MD5加密过的username,client_id,scope',
`user_name` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '登录的用户名',
`client_id` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '客户端ID',
`authentication` blob NULL COMMENT 'OAuth2Authentication.java对象序列化后的二进制数据',
`refresh_token` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 'MD5加密后的refresh_token的值',
PRIMARY KEY (`authentication_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '访问令牌' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of oauth_access_token
-- ----------------------------
INSERT INTO `oauth_access_token` VALUES ('cfd26bb25194ad1ae6fe1a2b759c7ba6', 0xACED0005737200436F72672E737072696E676672616D65776F726B2E73656375726974792E6F61757468322E636F6D6D6F6E2E44656661756C744F4175746832416363657373546F6B656E0CB29E361B24FACE0200064C00156164646974696F6E616C496E666F726D6174696F6E74000F4C6A6176612F7574696C2F4D61703B4C000A65787069726174696F6E7400104C6A6176612F7574696C2F446174653B4C000C72656672657368546F6B656E74003F4C6F72672F737072696E676672616D65776F726B2F73656375726974792F6F61757468322F636F6D6D6F6E2F4F417574683252656672657368546F6B656E3B4C000573636F706574000F4C6A6176612F7574696C2F5365743B4C0009746F6B656E547970657400124C6A6176612F6C616E672F537472696E673B4C000576616C756571007E000578707372001E6A6176612E7574696C2E436F6C6C656374696F6E7324456D7074794D6170593614855ADCE7D002000078707372000E6A6176612E7574696C2E44617465686A81014B597419030000787077080000018079CB3BD3787372004C6F72672E737072696E676672616D65776F726B2E73656375726974792E6F61757468322E636F6D6D6F6E2E44656661756C744578706972696E674F417574683252656672657368546F6B656E2FDF47639DD0C9B70200014C000A65787069726174696F6E71007E0002787200446F72672E737072696E676672616D65776F726B2E73656375726974792E6F61757468322E636F6D6D6F6E2E44656661756C744F417574683252656672657368546F6B656E73E10E0A6354D45E0200014C000576616C756571007E0005787074002431643465383931652D343737362D346138362D623966372D6330643132623732376430347371007E00097708000001809DA0D15278737200256A6176612E7574696C2E436F6C6C656374696F6E7324556E6D6F6469666961626C65536574801D92D18F9B80550200007872002C6A6176612E7574696C2E436F6C6C656374696F6E7324556E6D6F6469666961626C65436F6C6C656374696F6E19420080CB5EF71E0200014C0001637400164C6A6176612F7574696C2F436F6C6C656374696F6E3B7870737200176A6176612E7574696C2E4C696E6B656448617368536574D86CD75A95DD2A1E020000787200116A6176612E7574696C2E48617368536574BA44859596B8B7340300007870770C000000103F40000000000001740003616C6C7874000662656172657274002430393934333030612D633966632D346233382D393732392D383761663261326562366462, '702834532a7bab38cab035bd2e8c3fee', NULL, 'client-app', 0xACED0005737200416F72672E737072696E676672616D65776F726B2E73656375726974792E6F61757468322E70726F76696465722E4F417574683241757468656E7469636174696F6EBD400B02166252130200024C000D73746F7265645265717565737474003C4C6F72672F737072696E676672616D65776F726B2F73656375726974792F6F61757468322F70726F76696465722F4F4175746832526571756573743B4C00127573657241757468656E7469636174696F6E7400324C6F72672F737072696E676672616D65776F726B2F73656375726974792F636F72652F41757468656E7469636174696F6E3B787200476F72672E737072696E676672616D65776F726B2E73656375726974792E61757468656E7469636174696F6E2E416273747261637441757468656E7469636174696F6E546F6B656ED3AA287E6E47640E0200035A000D61757468656E746963617465644C000B617574686F7269746965737400164C6A6176612F7574696C2F436F6C6C656374696F6E3B4C000764657461696C737400124C6A6176612F6C616E672F4F626A6563743B787000737200266A6176612E7574696C2E436F6C6C656374696F6E7324556E6D6F6469666961626C654C697374FC0F2531B5EC8E100200014C00046C6973747400104C6A6176612F7574696C2F4C6973743B7872002C6A6176612E7574696C2E436F6C6C656374696F6E7324556E6D6F6469666961626C65436F6C6C656374696F6E19420080CB5EF71E0200014C00016371007E00047870737200136A6176612E7574696C2E41727261794C6973747881D21D99C7619D03000149000473697A657870000000007704000000007871007E000C707372003A6F72672E737072696E676672616D65776F726B2E73656375726974792E6F61757468322E70726F76696465722E4F41757468325265717565737400000000000000010200075A0008617070726F7665644C000B617574686F72697469657371007E00044C000A657874656E73696F6E7374000F4C6A6176612F7574696C2F4D61703B4C000B72656469726563745572697400124C6A6176612F6C616E672F537472696E673B4C00077265667265736874003B4C6F72672F737072696E676672616D65776F726B2F73656375726974792F6F61757468322F70726F76696465722F546F6B656E526571756573743B4C000B7265736F7572636549647374000F4C6A6176612F7574696C2F5365743B4C000D726573706F6E7365547970657371007E0011787200386F72672E737072696E676672616D65776F726B2E73656375726974792E6F61757468322E70726F76696465722E426173655265717565737436287A3EA37169BD0200034C0008636C69656E74496471007E000F4C001172657175657374506172616D657465727371007E000E4C000573636F706571007E0011787074000A636C69656E742D617070737200256A6176612E7574696C2E436F6C6C656374696F6E7324556E6D6F6469666961626C654D6170F1A5A8FE74F507420200014C00016D71007E000E7870737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F400000000000037708000000040000000274000A6772616E745F74797065740012636C69656E745F63726564656E7469616C73740009636C69656E745F696474000A636C69656E742D61707078737200256A6176612E7574696C2E436F6C6C656374696F6E7324556E6D6F6469666961626C65536574801D92D18F9B80550200007871007E0009737200176A6176612E7574696C2E4C696E6B656448617368536574D86CD75A95DD2A1E020000787200116A6176612E7574696C2E48617368536574BA44859596B8B7340300007870770C000000103F40000000000001740003616C6C78017371007E0020770C000000103F40000000000000787371007E00173F40000000000000770800000010000000007870707371007E0020770C000000103F40000000000000787371007E0020770C000000103F400000000000007870, '1bcc81c9038d7305e2ca339a9a3732db');
SET FOREIGN_KEY_CHECKS = 1;
oauth_refresh_token
刷新token的记录表, token_id字段与oauth_access_token的refresh_token字段关联。
/*
Navicat Premium Data Transfer
Source Server : win-local
Source Server Type : MySQL
Source Server Version : 50737
Source Host : localhost:3306
Source Schema : oauth2
Target Server Type : MySQL
Target Server Version : 50737
File Encoding : 65001
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for oauth_refresh_token
-- ----------------------------
DROP TABLE IF EXISTS `oauth_refresh_token`;
CREATE TABLE `oauth_refresh_token` (
`token_id` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 'MD5加密过的refresh_token的值',
`token` blob NULL COMMENT 'OAuth2RefreshToken.java对象序列化后的二进制数据',
`authentication` blob NULL COMMENT 'OAuth2Authentication.java对象序列化后的二进制数据'
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '更新令牌' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of oauth_refresh_token
-- ----------------------------
INSERT INTO `oauth_refresh_token` VALUES ('1bcc81c9038d7305e2ca339a9a3732db', 0xACED00057372004C6F72672E737072696E676672616D65776F726B2E73656375726974792E6F61757468322E636F6D6D6F6E2E44656661756C744578706972696E674F417574683252656672657368546F6B656E2FDF47639DD0C9B70200014C000A65787069726174696F6E7400104C6A6176612F7574696C2F446174653B787200446F72672E737072696E676672616D65776F726B2E73656375726974792E6F61757468322E636F6D6D6F6E2E44656661756C744F417574683252656672657368546F6B656E73E10E0A6354D45E0200014C000576616C75657400124C6A6176612F6C616E672F537472696E673B787074002431643465383931652D343737362D346138362D623966372D6330643132623732376430347372000E6A6176612E7574696C2E44617465686A81014B59741903000078707708000001809DA0D15278, 0xACED0005737200416F72672E737072696E676672616D65776F726B2E73656375726974792E6F61757468322E70726F76696465722E4F417574683241757468656E7469636174696F6EBD400B02166252130200024C000D73746F7265645265717565737474003C4C6F72672F737072696E676672616D65776F726B2F73656375726974792F6F61757468322F70726F76696465722F4F4175746832526571756573743B4C00127573657241757468656E7469636174696F6E7400324C6F72672F737072696E676672616D65776F726B2F73656375726974792F636F72652F41757468656E7469636174696F6E3B787200476F72672E737072696E676672616D65776F726B2E73656375726974792E61757468656E7469636174696F6E2E416273747261637441757468656E7469636174696F6E546F6B656ED3AA287E6E47640E0200035A000D61757468656E746963617465644C000B617574686F7269746965737400164C6A6176612F7574696C2F436F6C6C656374696F6E3B4C000764657461696C737400124C6A6176612F6C616E672F4F626A6563743B787000737200266A6176612E7574696C2E436F6C6C656374696F6E7324556E6D6F6469666961626C654C697374FC0F2531B5EC8E100200014C00046C6973747400104C6A6176612F7574696C2F4C6973743B7872002C6A6176612E7574696C2E436F6C6C656374696F6E7324556E6D6F6469666961626C65436F6C6C656374696F6E19420080CB5EF71E0200014C00016371007E00047870737200136A6176612E7574696C2E41727261794C6973747881D21D99C7619D03000149000473697A657870000000007704000000007871007E000C707372003A6F72672E737072696E676672616D65776F726B2E73656375726974792E6F61757468322E70726F76696465722E4F41757468325265717565737400000000000000010200075A0008617070726F7665644C000B617574686F72697469657371007E00044C000A657874656E73696F6E7374000F4C6A6176612F7574696C2F4D61703B4C000B72656469726563745572697400124C6A6176612F6C616E672F537472696E673B4C00077265667265736874003B4C6F72672F737072696E676672616D65776F726B2F73656375726974792F6F61757468322F70726F76696465722F546F6B656E526571756573743B4C000B7265736F7572636549647374000F4C6A6176612F7574696C2F5365743B4C000D726573706F6E7365547970657371007E0011787200386F72672E737072696E676672616D65776F726B2E73656375726974792E6F61757468322E70726F76696465722E426173655265717565737436287A3EA37169BD0200034C0008636C69656E74496471007E000F4C001172657175657374506172616D657465727371007E000E4C000573636F706571007E0011787074000A636C69656E742D617070737200256A6176612E7574696C2E436F6C6C656374696F6E7324556E6D6F6469666961626C654D6170F1A5A8FE74F507420200014C00016D71007E000E7870737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F400000000000037708000000040000000274000A6772616E745F74797065740012636C69656E745F63726564656E7469616C73740009636C69656E745F696474000A636C69656E742D61707078737200256A6176612E7574696C2E436F6C6C656374696F6E7324556E6D6F6469666961626C65536574801D92D18F9B80550200007871007E0009737200176A6176612E7574696C2E4C696E6B656448617368536574D86CD75A95DD2A1E020000787200116A6176612E7574696C2E48617368536574BA44859596B8B7340300007870770C000000103F40000000000001740003616C6C78017371007E0020770C000000103F40000000000000787371007E00173F40000000000000770800000010000000007870707371007E0020770C000000103F40000000000000787371007E0020770C000000103F400000000000007870);
INSERT INTO `oauth_refresh_token` VALUES ('4c64de4ee4fe5845c6b793c58f39292b', 0xACED00057372004C6F72672E737072696E676672616D65776F726B2E73656375726974792E6F61757468322E636F6D6D6F6E2E44656661756C744578706972696E674F417574683252656672657368546F6B656E2FDF47639DD0C9B70200014C000A65787069726174696F6E7400104C6A6176612F7574696C2F446174653B787200446F72672E737072696E676672616D65776F726B2E73656375726974792E6F61757468322E636F6D6D6F6E2E44656661756C744F417574683252656672657368546F6B656E73E10E0A6354D45E0200014C000576616C75657400124C6A6176612F6C616E672F537472696E673B787074002464333337666233332D366462642D343563332D623138372D3265303164623539323065307372000E6A6176612E7574696C2E44617465686A81014B59741903000078707708000001809DC0526778, 0xACED0005737200416F72672E737072696E676672616D65776F726B2E73656375726974792E6F61757468322E70726F76696465722E4F417574683241757468656E7469636174696F6EBD400B02166252130200024C000D73746F7265645265717565737474003C4C6F72672F737072696E676672616D65776F726B2F73656375726974792F6F61757468322F70726F76696465722F4F4175746832526571756573743B4C00127573657241757468656E7469636174696F6E7400324C6F72672F737072696E676672616D65776F726B2F73656375726974792F636F72652F41757468656E7469636174696F6E3B787200476F72672E737072696E676672616D65776F726B2E73656375726974792E61757468656E7469636174696F6E2E416273747261637441757468656E7469636174696F6E546F6B656ED3AA287E6E47640E0200035A000D61757468656E746963617465644C000B617574686F7269746965737400164C6A6176612F7574696C2F436F6C6C656374696F6E3B4C000764657461696C737400124C6A6176612F6C616E672F4F626A6563743B787000737200266A6176612E7574696C2E436F6C6C656374696F6E7324556E6D6F6469666961626C654C697374FC0F2531B5EC8E100200014C00046C6973747400104C6A6176612F7574696C2F4C6973743B7872002C6A6176612E7574696C2E436F6C6C656374696F6E7324556E6D6F6469666961626C65436F6C6C656374696F6E19420080CB5EF71E0200014C00016371007E00047870737200136A6176612E7574696C2E41727261794C6973747881D21D99C7619D03000149000473697A657870000000007704000000007871007E000C707372003A6F72672E737072696E676672616D65776F726B2E73656375726974792E6F61757468322E70726F76696465722E4F41757468325265717565737400000000000000010200075A0008617070726F7665644C000B617574686F72697469657371007E00044C000A657874656E73696F6E7374000F4C6A6176612F7574696C2F4D61703B4C000B72656469726563745572697400124C6A6176612F6C616E672F537472696E673B4C00077265667265736874003B4C6F72672F737072696E676672616D65776F726B2F73656375726974792F6F61757468322F70726F76696465722F546F6B656E526571756573743B4C000B7265736F7572636549647374000F4C6A6176612F7574696C2F5365743B4C000D726573706F6E7365547970657371007E0011787200386F72672E737072696E676672616D65776F726B2E73656375726974792E6F61757468322E70726F76696465722E426173655265717565737436287A3EA37169BD0200034C0008636C69656E74496471007E000F4C001172657175657374506172616D657465727371007E000E4C000573636F706571007E0011787074000A636C69656E742D617070737200256A6176612E7574696C2E436F6C6C656374696F6E7324556E6D6F6469666961626C654D6170F1A5A8FE74F507420200014C00016D71007E000E7870737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F400000000000067708000000080000000374000A6772616E745F7479706574000870617373776F7264740009636C69656E745F696474000A636C69656E742D617070740008757365726E616D6574000462696E6778737200256A6176612E7574696C2E436F6C6C656374696F6E7324556E6D6F6469666961626C65536574801D92D18F9B80550200007871007E0009737200176A6176612E7574696C2E4C696E6B656448617368536574D86CD75A95DD2A1E020000787200116A6176612E7574696C2E48617368536574BA44859596B8B7340300007870770C000000103F40000000000001740003616C6C78017371007E0022770C000000103F40000000000000787371007E00173F40000000000000770800000010000000007870707371007E0022770C000000103F40000000000000787371007E0022770C000000103F40000000000000787372004F6F72672E737072696E676672616D65776F726B2E73656375726974792E61757468656E7469636174696F6E2E557365726E616D6550617373776F726441757468656E7469636174696F6E546F6B656E00000000000002300200024C000B63726564656E7469616C7371007E00054C00097072696E636970616C71007E00057871007E0003017371007E00077371007E000B000000007704000000007871007E002C737200176A6176612E7574696C2E4C696E6B6564486173684D617034C04E5C106CC0FB0200015A000B6163636573734F726465727871007E00173F400000000000067708000000080000000474000D636C69656E745F73656372657474001161736466686F6C7531326A6F736164662371007E001971007E001A71007E001B71007E001C71007E001D71007E001E780070737200326F72672E737072696E676672616D65776F726B2E73656375726974792E636F72652E7573657264657461696C732E5573657200000000000002300200075A00116163636F756E744E6F6E457870697265645A00106163636F756E744E6F6E4C6F636B65645A001563726564656E7469616C734E6F6E457870697265645A0007656E61626C65644C000B617574686F72697469657371007E00114C000870617373776F726471007E000F4C0008757365726E616D6571007E000F7870010101017371007E001F737200116A6176612E7574696C2E54726565536574DD98509395ED875B0300007870737200466F72672E737072696E676672616D65776F726B2E73656375726974792E636F72652E7573657264657461696C732E5573657224417574686F72697479436F6D70617261746F7200000000000002300200007870770400000000787071007E001E);
SET FOREIGN_KEY_CHECKS = 1;
sys_user
系统用户表, 主要包含用户信息,密码采用BCryptPasswordEncoder加密方法encode后的结果。
/*
Navicat Premium Data Transfer
Source Server : win-local
Source Server Type : MySQL
Source Server Version : 50737
Source Host : localhost:3306
Source Schema : oauth2
Target Server Type : MySQL
Target Server Version : 50737
File Encoding : 65001
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for sys_user
-- ----------------------------
DROP TABLE IF EXISTS `sys_user`;
CREATE TABLE `sys_user` (
`id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT,
`user_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`pass_word` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`nick_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`sex` int(2) NULL DEFAULT NULL,
`phone` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`enable` int(2) NULL DEFAULT NULL,
`email` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`avatar` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`create_time` datetime(0) NULL DEFAULT NULL,
`account_expired` tinyint(1) UNSIGNED NULL DEFAULT 0 COMMENT '账号是否失效',
`account_locked` tinyint(1) UNSIGNED NULL DEFAULT 0 COMMENT '账号是否被锁定',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `user_name`(`user_name`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
-- ----------------------------
-- Records of sys_user
-- ----------------------------
INSERT INTO `sys_user` VALUES (1, 'admin', '$2a$10$gHF4b6c.Z4LrSqly3kpLwegXzBo6Q1TtBQlcrXt6Lctu/aLAZlPQe', 'bingbing', 1, '123', 1, '123456@qq.com', '', '2022-04-28 11:02:15', 0, 0);
INSERT INTO `sys_user` VALUES (2, 'bing', '$2a$10$FgJjpqW0WihRn/aYXxh8QuO0vp2iGK268H/G8VmbF5kRXeK5b23UG', 'bing', 1, '131', 1, '', '', '2022-04-28 18:25:32', 0, 0);
INSERT INTO `sys_user` VALUES (3, 'test01', '4b15d2b3b671209e01202331881af5a6044d342dc624d29a53ed6b4402af6d61', 'test', 1, '2312', 1, '1212', NULL, NULL, 0, 0);
SET FOREIGN_KEY_CHECKS = 1;
1) pom.xml
核心依赖: spring-cloud-starter-oauth2, 版本尽量选择较新的。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.bing.cloud</groupId>
<artifactId>oauth2-project</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>oauth2-server</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>oauth2-server</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.46</version>
</dependency>
<!-- redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--整合mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.4</version>
</dependency>
<!-- mybatis-plus插件-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.2.0</version>
</dependency>
<!--集成druid连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--security -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
<version>2.2.4.RELEASE</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
2) application.yml
配置mysql数据源、nacos 注册中心、redis、mybatis, 注意配置spring.application.name, spring.application.name作为微服务名字注册到nacos里。
server:
port: 9010
servlet:
context-path: /
spring:
application:
name: oauth2-server-service
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/oauth2?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
username: root
password: root
cloud:
nacos:
discovery:
server-addr: localhost:8848
username: nacos
password: nacos
redis:
host: localhost
port: 6379
password: redis#847652.
database: 0
management:
endpoints:
web:
exposure:
include: "*"
#mybatis
mybatis:
mapper-locations:
- classpath:mapper/*.xml
- classpath:com/**/mapper/*.xml
# myabtis-domain
mybatis-plus:
type-aliases-package: com.bing.cloud.**.entity
3) 配置授权服务器
- 配置ClientDetailService, 使一些配置支持从数据库表oauth_client_details读取。
- 配置自定义的userDetailService, 通过实现UserDetailsService接口创建。
- 配置JdbcTokenStore, 将生成的token落库,如果不配置,那么在生成token和验证token时会默认使用InMemoryTokenStore, 生成的token会存放在一个accessTokenStore的concurrentHashMap里,也就是说默认是基于内存认证的,我们可以通过配置JdbcTokenStore基于数据库认证,如果采用此方式,必须要用到oauth_access_token表。
- TokenStore也可以选择使用RedisTokenStore, redis 速度会比内存更高效,tokenStore只能选择一种。
- 配置允许使用表单认证,.tokenKeyAccess("permitAll()") .checkTokenAccess("permitAll()"), 如果没有该配置,通过网关访问user-service转发请求/oauth/check_token的时候会出现403的问题。
package com.bing.cloud.config;
import com.bing.cloud.service.impl.CustomUserDetailServiceImpl;
import lombok.AllArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.token.store.JdbcTokenStore;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;
import javax.sql.DataSource;
/**
* 认证服务器配置
*/
@AllArgsConstructor
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
private final CustomUserDetailServiceImpl userDetailsService;
private final AuthenticationManager authenticationManager;
// private final PasswordEncoder passwordEncoder;
@Autowired
private DataSource dataSource;
@Autowired
private RedisConnectionFactory redisConnectionFactory;
/**
* 基于数据库认证
*
* @return
*/
@Bean
public ClientDetailsService customClientDetailsService() {
return new JdbcClientDetailsService(dataSource);
}
/**
* token 落库
*
* @return
*/
public JdbcTokenStore jdbcTokenStore() {
return new JdbcTokenStore(dataSource);
}
public RedisTokenStore redisTokenStore(){
return new RedisTokenStore(redisConnectionFactory);
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.withClientDetails(customClientDetailsService());
// 由于配置了JDBCClient, 因此会去查询数据库oauth_client_details,因此下面的配置可以不需要
// .withClient("client-app")
// .secret(passwordEncoder.encode("asdfholu12josadf#"))
// .autoApprove(true)
// .redirectUris("http://127.0.0.1:9010/dashboard")
// .scopes("all")
// .authorizedGrantTypes("password", "implicit", "client_credentials", "authorization_code","refresh_token")
// .accessTokenValiditySeconds(3600)
// .refreshTokenValiditySeconds(86400);
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
//配置加载用户信息的服务
// token 落库
endpoints.authenticationManager(authenticationManager)
.userDetailsService(userDetailsService)
.tokenStore(redisTokenStore())
;
}
/**
* 解决访问/oauth/check_token 403的问题
*
* @param security
* @throws Exception
*/
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
// 允许表单认证
security
.tokenKeyAccess("permitAll()")
.checkTokenAccess("permitAll()")
.allowFormAuthenticationForClients();
}
}
4) 配置资源服务器
auth-server-service对外提供的api也可以看做资源,同样支持授权和认证。
package com.bing.cloud.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
/**
* 资源服务器配置
*/
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.requestMatchers()
.antMatchers("/api/**");//配置需要保护的资源路径
}
}
5) 配置HttpSecurity
可以在WebSecurityConfigurerAdapter里configure方法里配置不需要拦截的url, 比如一些登录、登出地址, 默认登录页面用 .formLogin()。
package com.bing.cloud.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
/**
* SpringSecurity配置
*/
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
public void configure(HttpSecurity http) throws Exception {
http.csrf()
.disable()
.httpBasic()
.and()
.authorizeRequests()
// 配置可以直接访问的页面
.antMatchers("/login/**", "/logout/**", "/api/getCurrentUser")
.permitAll()
// 其余所有请求都要通过认证鉴权
.anyRequest()
.authenticated()
.and()
// 配置spring security默认的登录页面
.formLogin()
.permitAll();
}
}
6) 集成mybatis基于数据库认证
当使用密码模式请求/oauth/token接口时,我们可以使用自己创建的数据库与oauth2进行集成,只需要重写UserDetailService里的loadUserByUserName(String username)方法即可。
创建SecurityUser对象,使用mybatisplus插件映射到sys_user表。
package com.bing.cloud.service.impl;
import com.bing.cloud.MessageConstant;
import com.bing.cloud.entity.SecurityUser;
import com.bing.cloud.service.UserService;
import com.google.gson.GsonBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AccountExpiredException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.LockedException;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;
/**
* 自定义用户服务
*/
@Service
public class CustomUserDetailServiceImpl implements UserDetailsService {
@Autowired
private UserService userService;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// 基于数据库认证
SecurityUser userInfo = userService.selectUserByUserName(username);
if (!userInfo.isEnabled()) {
throw new DisabledException(MessageConstant.ACCOUNT_DISABLED);
} else if (userInfo.isAccountLocked()) {
throw new LockedException(MessageConstant.ACCOUNT_LOCKED);
} else if (userInfo.isAccountExpired()) {
throw new AccountExpiredException(MessageConstant.ACCOUNT_EXPIRED);
}
// 获取到所有的role, 把role写入到simpleGrantedAuthority里。
List<SimpleGrantedAuthority> authorities = new ArrayList<>();
// String password = passwordEncoder.encode(userInfo.getPassword());
return new User(username, userInfo.getPassword(), authorities);
}
}
如果需要配置一些role, 可以通过mybatis将role表里的角色添加到SimpleGrantedAuthority列表里。
上述操作如果无误后启动 Auth-Server,能在nacos 里发现注册成功即可。
3. 整合网关gateway
如果还有不了解gateway的朋友,推荐抽点时间浏览一下以下的文章
微服务架构网关组件Spring Cloud Gateway 用法详解和实战案例_Dream_it_possible!的博客-CSDN博客
1) 创建一个网关服务实现请求转发到auth-server
本节目标是创建一个网关服务,实现请求能通过nacos转发到auth-server服务器上。
网关的作用是为了统一请求的入口,因此不建议在网关里写入大量的鉴权逻辑,新建一个springboot工程
2) pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example.gateway</groupId>
<artifactId>cloud-gateway</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>cloud-gateway</name>
<description>project for Spring Boot</description>
<properties>
<java.version>11</java.version>
<java.version>11</java.version>
<docker.registry.url>116.62.146.90</docker.registry.url>
<docker.namespace>my-shop</docker.namespace>
<spring-cloud.version>2021.0.1</spring-cloud.version>
<spring.cloud.alibaba.version>2021.0.1.0</spring.cloud.alibaba.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
<!--整合断路器-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<version>2.2.4.RELEASE</version>
</dependency>
<!-- 添加eureka客户端-->
<!-- <dependency>-->
<!-- <groupId>org.springframework.cloud</groupId>-->
<!-- <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>-->
<!-- </dependency>-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring.cloud.alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<finalName>my-gateway</finalName>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>com.spotify</groupId>
<artifactId>dockerfile-maven-plugin</artifactId>
<version>1.4.13</version>
<executions>
<execution>
<id>build_image</id>
<phase>package</phase>
<goals>
<!--如果package时不想用docker打包,就注释掉这个goal -->
<goal>build</goal>
<goal>push</goal>
</goals>
</execution>
</executions>
<configuration>
<contextDirectory>${project.basedir}</contextDirectory>
<useMavenSettingsForAuth>true</useMavenSettingsForAuth>
<repository>${docker.registry.url}/${docker.namespace}/${project.artifactId}</repository>
<tag>${project.version}</tag>
<buildArgs>
<JAR_FILE>${project.build.finalName}.jar</JAR_FILE>
</buildArgs>
</configuration>
</plugin>
</plugins>
</build>
</project>
3) 配置路由
添加auth-service-routte路由,注意将uri设置为lb://oauth2-server-service,因为通过nacos转发时,会根据服务名进行负载均衡式的请求。
可以在predicates里配置StripPrefix=1, 转发到目标服务后会去掉Path里第一个"/"和第二个"/"里的内容。
server:
port: 9000
spring:
application:
name: gateway
cloud:
gateway:
discovery:
locator:
enabled: true
lower-case-service-id: true
routes:
- id: user-service #路由的ID
uri: lb://user-service
predicates:
- Method=GET,POST
- Path=/user/**
filters:
- StripPrefix=1
- name: Retry
args:
retires: 1
statuses: BAD_GATEWAY
- id: auth-service-route
uri: lb://oauth2-server-service
predicates:
- Method=GET,POST
- Path=/auth-server/**
filters:
- name: Retry
args:
retries: 1 # 调用失败需要进行重试的次数,比如因为网络原因出现502等
statuses: BAD_GATEWAY
- StripPrefix=1
nacos:
server-addr: localhost:8848
discovery:
server-addr: ${spring.cloud.nacos.server-addr}
username: nacos
password: nacos
namespace: public
logging:
level:
org.springframework.cloud.gateway: debug
启动网关服务, 可以从控制台看到我们配置的路由和相关的规则。
4) 测试网关转发请求
首先在auth-server添加一个测试api: /api/hello,使用postman访问请求localhost:9000/auth-server/api/hello。
解决用网关转发Lb时,一直报503的问题:
由于最新版spring cloud的gateway不支持负载均衡功能,因此需要手动添加一个loadbalancer依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-loadbalancer</artifactId>
</dependency>
重新刷新依赖、启动网关微服务,访问localhost:9000/auth-server/api/hello,响应代码为: 401, 说明请求正确的转发到auth-server:
在一个大型的分布式系统里,存在的微服务可能有几十个或者上百个,如何使用网关和oauth server来保护,我们可以将普通微服务的未认证的请求全部由网关转发给鉴权服务器。
四、将普通微服务配置为资源服务器由网关统一转发认证
1. 创建user-service
同样集成nacos, oauth2,mybatis 等框架,运行起来后能在nacos中发现。
2. 配置资源服务器
放了方便配置资源服务器,我们将所有需要认证的接口都带上/api前缀。
package com.bingbing.sh.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
/**
* 资源服务器配置
*/
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.requestMatchers()
.antMatchers("/api/**");//配置需要保护的资源路径
}
}
3. 配置oauth2 client 统一转发请求
需要注意client-id和client-secret要与数据库里保持一致,可以选择使用客户端模式的进行认证,另外ecurity.oauth2.resource.token-info-uri这个值配为:oauth2-server的/oauth/check_token。
在application.properties文件里添加以下配置:
security.oauth2.client.access-token-uri=http://localhost:9000/auth-server/oauth/token
security.oauth2.client.client-authentication-scheme=form
security.oauth2.client.client-id=client-app
security.oauth2.client.client-secret=asdfholu12josadf#
security.oauth2.client.grant-type=client_credentials
security.oauth2.resource.token-info-uri=http://localhost:9000/auth-server/oauth/check_token
org.springframework.security.oauth2.provider.authentication
包里。 .doFilter() 方法, doFilter()方法用来拦截进入的请求。
接着进入到OAuth2AuthenticationManager.authenticate方法:
然后进入到tokenServices.loadAuthentication()方法:
默认进入到DefaultTokenService里的loadAuthentication:
可以发现默认是进入到DefaultTokenService认证token的,我们可以推断出肯定是不行的,因为我在user-service的项目里没有配置授权服务器,简单讲user-service是没有授权功能的。
接着我们把注释的配置放开,然后进入到loadAuthentication方法,发现是通过RemoteTokenServices调用的,向我们配置的security.oauth2.resource.token-info-uri 地址发起了一个rest请求:
如果token是正确的话,/oauth/check_token接口会返回一个map给我们
五、统一鉴权的四种模式验证
我们需要基于数据库的进行认证,那么需要在数据库里也配置所有的授权模式。
在authorized_grant_types字段里配置4种模式,用"," 隔开: password,client_credentials,authorization_code,implicit,在配置可以在加一个refresh_token,用于支持刷新token。
1. 授权码模式(authorization_code)
授权码模式是现在最严密、最安全的一种认证模式,第三方应用先通过登录获取到一个授权码和directUrl, 然后携带授权码和directUrl去授权服务器请求token令牌, 其中direct_url配置在数据库里,参数中的grant_type为client_credentials。
第一步在浏览器中访问如下地址:
http://localhost:9010/oauth/authorize?client_id=client-app&client_secret=asdfholu12josadf%23&response_type=code
输入用户名和密码:
登录成功后会在地址栏上返回一个code,每次申请的code只能使用一次, 由于我没有配置/dashboard页面,因此会报404的错误:
拿到code后,使用postman采用授权码模式请求token:
再次用同样的code去请求就会报错:Invalid authorization code: n2jZMX,必须重新访问/oauth/authorize重新申请一个新的code才行。
2. 简化模式(implicit)
简化模式是直接在浏览器中向授权服务器申请令牌,成功了就能拿到token,参数中的grant_type为token。
第一步在浏览器中输入:
localhost:9010/oauth/authorize?grant_type=implicit&client_id=client-app&scope=all&redirect_uri=http://127.0.0.1:9010/dashboard&response_type=token
其中 redirect_uri和grant_type均配置在数据库里,如果没有登录,那么会先输入用户名和密码,登录成功后,会在地址栏上显示出token和失效时间。
3. 密码模式(password)
密码模式是用户向客户端提供用户名和密码,客户端使用这些信息向授权服务器去申请令牌,参数中的grant_type为password,需要客户端应用与服务器提供商高度信息,必须是同一家公司。
密码模式直接将客户端ID、secret、用户名和密码放在body的表单里,用postman发一个post请求。
localhost:9010/oauth/token
body里的参数
grant_type:password
client_id:client-app
client_secret:asdfholu12josadf#
username:bing
password:123456
4. 客户端模式(client_credentials)
客户端模式是指客户端用自己的名义而不是用户的身份去向授权服务器申请令牌。
用postman请求localhost:9010/oauth/token, body里的参数为:
grant_type:client_credentials
client_id:client-app
client_secret:asdfholu12josadf#
以上就是搭建统一鉴权的所有流程,4种模式都可以使用,可根据不同的场景进行甄选。
参考:
11.授权认证Oauth2_C--G的博客-CSDN博客
转载请注明:最新版微服务架构鉴权解决方案Spring Cloud Gateway + Oauth2.0+mybatis+mysql+redis+nacos 统一认证和鉴权 | 胖虎的工具箱-编程导航