微服务权限终极解决方案,Spring Cloud Gateway + Oauth2 实现统一认证和鉴权!

>>强大,10k+点赞的 SpringBoot 后台管理系统竟然出了详细教程!

微服务权限终极解决方案,Spring Cloud Gateway + Oauth2 实现统一认证和鉴权!

点击加入:
后端技术内卷群,一起学习!

鉴权架构图

微服务权限终极解决方案,Spring Cloud Gateway + Oauth2 实现统一认证和鉴权!

一、服务列表和技术版本列表

1. 服务列表

微服务权限终极解决方案,Spring Cloud Gateway + Oauth2 实现统一认证和鉴权!

2. 技术版本列表

微服务权限终极解决方案,Spring Cloud Gateway + Oauth2 实现统一认证和鉴权!微服务权限终极解决方案,Spring Cloud Gateway + Oauth2 实现统一认证和鉴权!

二、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。

微服务权限终极解决方案,Spring Cloud Gateway + Oauth2 实现统一认证和鉴权!通过表单的形式提交, 通过后会返回一个带期限的access_token, 如下用客户端模式去申请token:

微服务权限终极解决方案,Spring Cloud Gateway + Oauth2 实现统一认证和鉴权!
2) 校验token接口 /oauth/check_token

/oauth/check_token接口在org.springframework.security.oauth2.provider.endpoint包里的CheckTokenEndpoint类里,请求方式get和post都可以,可以看到需要一个token参数

微服务权限终极解决方案,Spring Cloud Gateway + Oauth2 实现统一认证和鉴权!校验成功后会返回客户端信息:

微服务权限终极解决方案,Spring Cloud Gateway + Oauth2 实现统一认证和鉴权!
3) 授权接口/oauth/authorize

/oauth/authorize接口在org.springframework.security.oauth2.provider.endpoint包里的AuthorizationEndpoint, 该接口可用于授权模式的授权操作, 也可用于简化模式的直接申请token。

2. Oauth2 定义的角色

  • 授权服务器: 给具有权限的资源拥有者对应的访问请求授权。

  • 资源服务器: 受保护的资源,可以为静态资源、接口等。

  • 资源拥有者: 具有该系统资源的拥有者,最终受益于用户。

  • 客户端: 与用户和资源、授权服务器沟通的平台,如一个web应用客户端,QQ、微信以第三方形式登录的客户端。

理解了Oauth2的概念后,我们把注册中心、授权服务器、网关搭起来。

三、环境搭建

1. 启动Nacos注册中心

可以参考以下文章启动nacos-server

https://blog.csdn.net/qq_33036061/article/details/124293067

2. 创建Oauth2 Server 工程并注册到Nacos

微服务权限终极解决方案,Spring Cloud Gateway + Oauth2 实现统一认证和鉴权!

数据库设计

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(128CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '客户端ID',
  `resource_ids` varchar(256CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '资源ID集合,多个资源时用英文逗号分隔',
  `client_secret` varchar(256CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '客户端密匙',
  `scope` varchar(256CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '客户端申请的权限范围',
  `authorized_grant_types` varchar(256CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '客户端支持的grant_type',
  `web_server_redirect_uri` varchar(256CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '重定向URI',
  `authorities` varchar(256CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '客户端所拥有的SpringSecurity的权限值,多个用英文逗号分隔',
  `access_token_validity` int(11NULL DEFAULT NULL COMMENT '访问令牌有效时间值(单位秒)',
  `refresh_token_validity` int(11NULL DEFAULT NULL COMMENT '更新令牌有效时间值(单位秒)',
  `additional_information` varchar(4096CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '预留字段',
  `autoapprove` varchar(256CHARACTER 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'NULL3600604800NULL'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(256CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 'MD5加密的access_token的值',
  `token` blob NULL COMMENT 'OAuth2AccessToken.java对象序列化后的二进制数据',
  `authentication_id` varchar(256CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT 'MD5加密过的username,client_id,scope',
  `user_name` varchar(256CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '登录的用户名',
  `client_id` varchar(256CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '客户端ID',
  `authentication` blob NULL COMMENT 'OAuth2Authentication.java对象序列化后的二进制数据',
  `refresh_token` varchar(256CHARACTER 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_tokenrefresh_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(256CHARACTER 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'0xACED00057372004C6F72672E737072696E676672616D65776F726B2E73656375726974792E6F61757468322E636F6D6D6F6E2E44656661756C744578706972696E674F417574683252656672657368546F6B656E2FDF47639DD0C9B70200014C000A65787069726174696F6E7400104C6A6176612F7574696C2F446174653B787200446F72672E737072696E676672616D65776F726B2E73656375726974792E6F61757468322E636F6D6D6F6E2E44656661756C744F417574683252656672657368546F6B656E73E10E0A6354D45E0200014C000576616C75657400124C6A6176612F6C616E672F537472696E673B787074002431643465383931652D343737362D346138362D623966372D6330643132623732376430347372000E6A6176612E7574696C2E44617465686A81014B59741903000078707708000001809DA0D152780xACED0005737200416F72672E737072696E676672616D65776F726B2E73656375726974792E6F61757468322E70726F76696465722E4F417574683241757468656E7469636174696F6EBD400B02166252130200024C000D73746F7265645265717565737474003C4C6F72672F737072696E676672616D65776F726B2F73656375726974792F6F61757468322F70726F76696465722F4F4175746832526571756573743B4C00127573657241757468656E7469636174696F6E7400324C6F72672F737072696E676672616D65776F726B2F73656375726974792F636F72652F41757468656E7469636174696F6E3B787200476F72672E737072696E676672616D65776F726B2E73656375726974792E61757468656E7469636174696F6E2E416273747261637441757468656E7469636174696F6E546F6B656ED3AA287E6E47640E0200035A000D61757468656E746963617465644C000B617574686F7269746965737400164C6A6176612F7574696C2F436F6C6C656374696F6E3B4C000764657461696C737400124C6A6176612F6C616E672F4F626A6563743B787000737200266A6176612E7574696C2E436F6C6C656374696F6E7324556E6D6F6469666961626C654C697374FC0F2531B5EC8E100200014C00046C6973747400104C6A6176612F7574696C2F4C6973743B7872002C6A6176612E7574696C2E436F6C6C656374696F6E7324556E6D6F6469666961626C65436F6C6C656374696F6E19420080CB5EF71E0200014C00016371007E00047870737200136A6176612E7574696C2E41727261794C6973747881D21D99C7619D03000149000473697A657870000000007704000000007871007E000C707372003A6F72672E737072696E676672616D65776F726B2E73656375726974792E6F61757468322E70726F76696465722E4F41757468325265717565737400000000000000010200075A0008617070726F7665644C000B617574686F72697469657371007E00044C000A657874656E73696F6E7374000F4C6A6176612F7574696C2F4D61703B4C000B72656469726563745572697400124C6A6176612F6C616E672F537472696E673B4C00077265667265736874003B4C6F72672F737072696E676672616D65776F726B2F73656375726974792F6F61757468322F70726F76696465722F546F6B656E526571756573743B4C000B7265736F7572636549647374000F4C6A6176612F7574696C2F5365743B4C000D726573706F6E7365547970657371007E0011787200386F72672E737072696E676672616D65776F726B2E73656375726974792E6F61757468322E70726F76696465722E426173655265717565737436287A3EA37169BD0200034C0008636C69656E74496471007E000F4C001172657175657374506172616D657465727371007E000E4C000573636F706571007E0011787074000A636C69656E742D617070737200256A6176612E7574696C2E436F6C6C656374696F6E7324556E6D6F6469666961626C654D6170F1A5A8FE74F507420200014C00016D71007E000E7870737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F400000000000037708000000040000000274000A6772616E745F74797065740012636C69656E745F63726564656E7469616C73740009636C69656E745F696474000A636C69656E742D61707078737200256A6176612E7574696C2E436F6C6C656374696F6E7324556E6D6F6469666961626C65536574801D92D18F9B80550200007871007E0009737200176A6176612E7574696C2E4C696E6B656448617368536574D86CD75A95DD2A1E020000787200116A6176612E7574696C2E48617368536574BA44859596B8B7340300007870770C000000103F40000000000001740003616C6C78017371007E0020770C000000103F40000000000000787371007E00173F40000000000000770800000010000000007870707371007E0020770C000000103F40000000000000787371007E0020770C000000103F400000000000007870);
INSERT INTO `oauth_refresh_token` VALUES ('4c64de4ee4fe5845c6b793c58f39292b'0xACED00057372004C6F72672E737072696E676672616D65776F726B2E73656375726974792E6F61757468322E636F6D6D6F6E2E44656661756C744578706972696E674F417574683252656672657368546F6B656E2FDF47639DD0C9B70200014C000A65787069726174696F6E7400104C6A6176612F7574696C2F446174653B787200446F72672E737072696E676672616D65776F726B2E73656375726974792E6F61757468322E636F6D6D6F6E2E44656661756C744F417574683252656672657368546F6B656E73E10E0A6354D45E0200014C000576616C75657400124C6A6176612F6C616E672F537472696E673B787074002464333337666233332D366462642D343563332D623138372D3265303164623539323065307372000E6A6176612E7574696C2E44617465686A81014B59741903000078707708000001809DC05267780xACED0005737200416F72672E737072696E676672616D65776F726B2E73656375726974792E6F61757468322E70726F76696465722E4F417574683241757468656E7469636174696F6EBD400B02166252130200024C000D73746F7265645265717565737474003C4C6F72672F737072696E676672616D65776F726B2F73656375726974792F6F61757468322F70726F76696465722F4F4175746832526571756573743B4C00127573657241757468656E7469636174696F6E7400324C6F72672F737072696E676672616D65776F726B2F73656375726974792F636F72652F41757468656E7469636174696F6E3B787200476F72672E737072696E676672616D65776F726B2E73656375726974792E61757468656E7469636174696F6E2E416273747261637441757468656E7469636174696F6E546F6B656ED3AA287E6E47640E0200035A000D61757468656E746963617465644C000B617574686F7269746965737400164C6A6176612F7574696C2F436F6C6C656374696F6E3B4C000764657461696C737400124C6A6176612F6C616E672F4F626A6563743B787000737200266A6176612E7574696C2E436F6C6C656374696F6E7324556E6D6F6469666961626C654C697374FC0F2531B5EC8E100200014C00046C6973747400104C6A6176612F7574696C2F4C6973743B7872002C6A6176612E7574696C2E436F6C6C656374696F6E7324556E6D6F6469666961626C65436F6C6C656374696F6E19420080CB5EF71E0200014C00016371007E00047870737200136A6176612E7574696C2E41727261794C6973747881D21D99C7619D03000149000473697A657870000000007704000000007871007E000C707372003A6F72672E737072696E676672616D65776F726B2E73656375726974792E6F61757468322E70726F76696465722E4F41757468325265717565737400000000000000010200075A0008617070726F7665644C000B617574686F72697469657371007E00044C000A657874656E73696F6E7374000F4C6A6176612F7574696C2F4D61703B4C000B72656469726563745572697400124C6A6176612F6C616E672F537472696E673B4C00077265667265736874003B4C6F72672F737072696E676672616D65776F726B2F73656375726974792F6F61757468322F70726F76696465722F546F6B656E526571756573743B4C000B7265736F7572636549647374000F4C6A6176612F7574696C2F5365743B4C000D726573706F6E7365547970657371007E0011787200386F72672E737072696E676672616D65776F726B2E73656375726974792E6F61757468322E70726F76696465722E426173655265717565737436287A3EA37169BD0200034C0008636C69656E74496471007E000F4C001172657175657374506172616D657465727371007E000E4C000573636F706571007E0011787074000A636C69656E742D617070737200256A6176612E7574696C2E436F6C6C656374696F6E7324556E6D6F6469666961626C654D6170F1A5A8FE74F507420200014C00016D71007E000E7870737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F400000000000067708000000080000000374000A6772616E745F7479706574000870617373776F7264740009636C69656E745F696474000A636C69656E742D617070740008757365726E616D6574000462696E6778737200256A6176612E7574696C2E436F6C6C656374696F6E7324556E6D6F6469666961626C65536574801D92D18F9B80550200007871007E0009737200176A6176612E7574696C2E4C696E6B656448617368536574D86CD75A95DD2A1E020000787200116A6176612E7574696C2E48617368536574BA44859596B8B7340300007870770C000000103F40000000000001740003616C6C78017371007E0022770C000000103F40000000000000787371007E00173F40000000000000770800000010000000007870707371007E0022770C000000103F40000000000000787371007E0022770C000000103F40000000000000787372004F6F72672E737072696E676672616D65776F726B2E73656375726974792E61757468656E7469636174696F6E2E557365726E616D6550617373776F726441757468656E7469636174696F6E546F6B656E00000000000002300200024C000B63726564656E7469616C7371007E00054C00097072696E636970616C71007E00057871007E0003017371007E00077371007E000B000000007704000000007871007E002C737200176A6176612E7574696C2E4C696E6B6564486173684D617034C04E5C106CC0FB0200015A000B6163636573734F726465727871007E00173F400000000000067708000000080000000474000D636C69656E745F73656372657474001161736466686F6C7531326A6F736164662371007E001971007E001A71007E001B71007E001C71007E001D71007E001E780070737200326F72672E737072696E676672616D65776F726B2E73656375726974792E636F72652E7573657264657461696C732E5573657200000000000002300200075A00116163636F756E744E6F6E457870697265645A00106163636F756E744E6F6E4C6F636B65645A001563726564656E7469616C734E6F6E457870697265645A0007656E61626C65644C000B617574686F72697469657371007E00114C000870617373776F726471007E000F4C0008757365726E616D6571007E000F7870010101017371007E001F737200116A6176612E7574696C2E54726565536574DD98509395ED875B0300007870737200466F72672E737072696E676672616D65776F726B2E73656375726974792E636F72652E7573657264657461696C732E5573657224417574686F72697479436F6D70617261746F7200000000000002300200007870770400000000787071007E001E);
 
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(20UNSIGNED NOT NULL AUTO_INCREMENT,
  `user_name` varchar(255CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `pass_word` varchar(255CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `nick_name` varchar(255CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `sex` int(2NULL DEFAULT NULL,
  `phone` varchar(255CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `enable` int(2NULL DEFAULT NULL,
  `email` varchar(255CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `avatar` varchar(255CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `create_time` datetime(0NULL DEFAULT NULL,
  `account_expired` tinyint(1UNSIGNED NULL DEFAULT 0 COMMENT '账号是否失效',
  `account_locked` tinyint(1UNSIGNED 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'00);
INSERT INTO `sys_user` VALUES (2'bing''$2a$10$FgJjpqW0WihRn/aYXxh8QuO0vp2iGK268H/G8VmbF5kRXeK5b23UG''bing'1'131'1'''''2022-04-28 18:25:32'00);
INSERT INTO `sys_user` VALUES (3'test01''4b15d2b3b671209e01202331881af5a6044d342dc624d29a53ed6b4402af6d61''test'1'2312'1'1212'NULLNULL00);
 
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 注册中心、redismybatis, 注意配置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会存放在一个accessTokenStoreconcurrentHashMap里,也就是说默认是基于内存认证的,我们可以通过配置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

可以在WebSecurityConfigurerAdapterconfigure方法里配置不需要拦截的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 里发现注册成功即可。

微服务权限终极解决方案,Spring Cloud Gateway + Oauth2 实现统一认证和鉴权!

3. 整合网关gateway

如果还有不了解gateway的朋友,推荐抽点时间浏览一下以下的文章

https://blog.csdn.net/qq_33036061/article/details/115935184

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

启动网关服务, 可以从控制台看到我们配置的路由和相关的规则。

微服务权限终极解决方案,Spring Cloud Gateway + Oauth2 实现统一认证和鉴权!

4) 测试网关转发请求

首先在auth-server添加一个测试api:  /api/hello,使用postman访问请求localhost:9000/auth-server/api/hello

微服务权限终极解决方案,Spring Cloud Gateway + Oauth2 实现统一认证和鉴权!解决用网关转发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:

微服务权限终极解决方案,Spring Cloud Gateway + Oauth2 实现统一认证和鉴权!在一个大型的分布式系统里,存在的微服务可能有几十个或者上百个,如何使用网关和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-idclient-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

我们在请求user-service里的接口时,可以跟踪一下源代码,看一下oauth2 在user-service做了哪些事?

首先把上述的properties里的配置给注释掉,进入OAuth2AuthenticationProcessingFilterorg.springframework.security.oauth2.provider.authentication 包里。 .doFilter() 方法, doFilter()方法用来拦截进入的请求。

微服务权限终极解决方案,Spring Cloud Gateway + Oauth2 实现统一认证和鉴权!接着进入到OAuth2AuthenticationManager.authenticate方法:

微服务权限终极解决方案,Spring Cloud Gateway + Oauth2 实现统一认证和鉴权!然后进入到tokenServices.loadAuthentication()方法:

微服务权限终极解决方案,Spring Cloud Gateway + Oauth2 实现统一认证和鉴权!默认进入到DefaultTokenService里的loadAuthentication:

微服务权限终极解决方案,Spring Cloud Gateway + Oauth2 实现统一认证和鉴权!可以发现默认是进入到DefaultTokenService认证token的,我们可以推断出肯定是不行的,因为我在user-service的项目里没有配置授权服务器,简单讲user-service是没有授权功能的。

接着我们把注释的配置放开,然后进入到loadAuthentication方法,发现是通过RemoteTokenServices调用的,向我们配置的security.oauth2.resource.token-info-uri 地址发起了一个rest请求:

微服务权限终极解决方案,Spring Cloud Gateway + Oauth2 实现统一认证和鉴权!如果token是正确的话,/oauth/check_token接口会返回一个map给我们

微服务权限终极解决方案,Spring Cloud Gateway + Oauth2 实现统一认证和鉴权!

五、统一鉴权的四种模式验证

我们需要基于数据库的进行认证,那么需要在数据库里也配置所有的授权模式。

微服务权限终极解决方案,Spring Cloud Gateway + Oauth2 实现统一认证和鉴权!authorized_grant_types字段里配置4种模式,用"," 隔开: password,client_credentials,authorization_code,implicit,在配置可以在加一个refresh_token,用于支持刷新token。

1. 授权码模式(authorization_code)

授权码模式是现在最严密、最安全的一种认证模式,第三方应用先通过登录获取到一个授权码和directUrl, 然后携带授权码和directUrl去授权服务器请求token令牌, 其中direct_url配置在数据库里,参数中的grant_typeclient_credentials

第一步在浏览器中访问如下地址:

http://localhost:9010/oauth/authorize?client_id=client-app&client_secret=asdfholu12josadf%23&response_type=code

输入用户名和密码:

微服务权限终极解决方案,Spring Cloud Gateway + Oauth2 实现统一认证和鉴权!登录成功后会在地址栏上返回一个code,每次申请的code只能使用一次, 由于我没有配置/dashboard页面,因此会报404的错误:

微服务权限终极解决方案,Spring Cloud Gateway + Oauth2 实现统一认证和鉴权!拿到code后,使用postman采用授权码模式请求token:

微服务权限终极解决方案,Spring Cloud Gateway + Oauth2 实现统一认证和鉴权!再次用同样的code去请求就会报错:Invalid authorization code: n2jZMX,必须重新访问/oauth/authorize重新申请一个新的code才行。

微服务权限终极解决方案,Spring Cloud Gateway + Oauth2 实现统一认证和鉴权!

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_urigrant_type均配置在数据库里,如果没有登录,那么会先输入用户名和密码,登录成功后,会在地址栏上显示出token和失效时间。

微服务权限终极解决方案,Spring Cloud Gateway + Oauth2 实现统一认证和鉴权!

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
微服务权限终极解决方案,Spring Cloud Gateway + Oauth2 实现统一认证和鉴权!

4. 客户端模式(client_credentials)

客户端模式是指客户端用自己的名义而不是用户的身份去向授权服务器申请令牌。

用postman请求localhost:9010/oauth/token, body里的参数为:

grant_type:client_credentials
client_id:client-app
client_secret:asdfholu12josadf#

微服务权限终极解决方案,Spring Cloud Gateway + Oauth2 实现统一认证和鉴权!以上就是搭建统一鉴权的所有流程,4种模式都可以使用,可根据不同的场景进行甄选。

来源:bingbing.blog.csdn.net/article/details/124443096

精彩推荐
最全的java面试题库
开源版的高仿 “ 微信 ”,吊炸天!

与其在网上拼命找题? 不如马上关注我们~

微服务权限终极解决方案,Spring Cloud Gateway + Oauth2 实现统一认证和鉴权!


↓点阅读原文,Java面试题库尽情看!

原文始发于微信公众号(Java面试题精选):微服务权限终极解决方案,Spring Cloud Gateway + Oauth2 实现统一认证和鉴权!