【java_wxid项目】【第七章】【Spring Cloud Security Oauth2集成】
主项目链接:https://gitee.com/java_wxid/java_wxid
项目架构及博文总结:
- 点击:【使用Spring Boot快速构建应用】
- 点击:【使用Spring Cloud Open Feign基于动态代理动态构造请求实现与其他系统进行交互】
- 点击:【使用Spring Cloud Hystrix实现服务容错、熔断、降级、监控】
- 点击:【使用Spring Cloud Ribbon以库的方式集成到服务的消费方实现客户端负载均衡】
- 点击:【使用Spring Cloud Gateway作为API网关服务进行请求拦截、服务分发、降级、限流】
- 点击:【使用Spring Cloud Security Oauth2作为微服务统一认证中心实现用户认证和授权访问】
- 点击:【使用Spring Cloud Stream作为消息驱动用于动态的切换中间件】
- 点击:【使用Spring Cloud Skywalking基于字节码注入通过探针方式进行链路追踪、分布式追踪、性能指标分析、应用和服务依赖分析】
- 点击:【使用Spring Cloud Alibaba Nacos实现服务注册/发现/续约/剔除/下线、心跳检测、服务配置管理、基于长轮训机制实现配置动态变更】
- 点击:【使用Spring Cloud Alibaba Seata作为对项目代码无入侵的分布式事务解决方案】
- 点击:【使用Spring Cloud Alibaba Sentinel实现高可用流量防护】
- 点击:【使用Apache ShardingSphere作为关系型数据库中间件实现分库分表、读写分离】
- 点击:【使用Apache Mybatis作为持久层框架用于定制化SQL、存储过程以及高级映射】
- 点击:【使用Redis作为高性能分布式缓存数据库】
- 点击:【使用ElasticSearch全文搜索】
- 点击:【使用MongoDB非关系型数据库】
- 点击:【使用xxl-job作为分布式任务调度平台】
- 点击:【使用Elasticsearch + Logstash + Kibana作为日志收集系统】
- 点击:【使用Apifox作为API文档、API调试、API Mock、API自动化测试】
- 点击:【使用Apache Spark作为基于内存计算的大数据分析引擎用于批处理、交互式查询】
- 点击:【使用ETL工具将数据源抽取到HDFS作为高可靠、高吞吐量的分布式文件系统存储,通过Hive清洗、处理和计算原始数据,Hive清洗处理后的结果,将存入Hbase,海量数据随机查询场景从HBase查询数据】
- 点击:【使用领域驱动DDD设计和设计模式进行开发】
- 点击:【使用Netty基于Java NIO封装的高性能的网络通信框架】
- 点击:【使用k8s、docker、docker-compose、宝塔面板进行环境搭建和部署】
- 点击:【使用Vue渐进式JavaScript框架作为适用场景丰富的Web前端框架】
- 点击:【分享人才筛选、工作分配、高效办公、项目推动等团队管理经验】
项目模块:
前期规划,实现部分
java_wxid
├── demo // 演示模块
│ └── 模块名称:apache-mybatis-demo模块 //Apache Mybatis集成(已实现并有博文总结)
│ └── 模块名称:apache-shardingsphere-demo模块 //Apache ShardingSphere集成(已实现并有博文总结)
│ └── 模块名称:design-demo模块 //设计模式实战落地(已实现并有博文总结)
│ └── 模块名称:elasticsearch-demo模块 //ElasticSearch集成(已实现并有博文总结)
│ └── 模块名称:mongodb-demo模块 //MongoDB集成(已实现并有博文总结)
│ └── 模块名称:redis-demo模块 //Redis集成(已实现并有博文总结)
│ └── 模块名称:spring-boot-demo模块 //Spring Boot快速构建应用(已实现并有博文总结)
│ └── 模块名称:spring-cloud-alibaba-nacos-demo模块 //Spring Cloud Alibaba Nacos集成(已实现并有博文总结)
│ └── 模块名称:spring-cloud-alibaba-seata-demo模块 //Spring Cloud Alibaba Seata集成(已实现并有博文总结)
│ └── 模块名称:spring-cloud-alibaba-sentinel-demo模块 //Spring Cloud Alibaba Sentinel集成(已实现并有博文总结)
│ └── 模块名称:spring-cloud-gateway-demo模块 //Spring Cloud Gateway集成(已实现并有博文总结)
│ └── 模块名称:spring-cloud-hystrix-demo模块 //Spring Cloud Hystrix集成(已实现并有博文总结)
│ └── 模块名称:spring-cloud-open-feign-demo模块 //Spring Cloud Open Feign集成(已实现并有博文总结)
│ └── 模块名称:spring-cloud-ribbon-demo模块 //Spring Cloud Ribbon集成(已实现并有博文总结)
│ └── 模块名称:spring-cloud-security-oauth2-demo模块 //Spring Cloud Security Oauth2集成(已实现并有博文总结)
│ └── 模块名称:spring-cloud-security-oauth2-sso-client-demo模块 //Spring Cloud Security Oauth2集成(已实现并有博文总结)
│ └── 模块名称:spring-cloud-skywalking-demo模块 //Spring Cloud Skywalking集成(已实现并有博文总结)
│ └── 模块名称:spring-cloud-stream-demo模块 //Spring Cloud Stream集成(已实现并有博文总结)
│ └── 模块名称:swagger-demo模块 //springfox-swagger2集成(已实现并有博文总结)
│ └── 模块名称:xxl-job模块 //xxl-job集成(已实现并有博文总结)
│ └── 模块名称:apache-spark-demo模块 //Apache Spark集成
│ └── 模块名称:etl-hdfs-hive-hbase-demo模块 //ETL、HDFS、Hive、Hbase集成
│ └── 模块名称:ddd-mode-demo模块 //DDD领域设计
│ └── 模块名称:netty-demo模块 //Netty集成
│ └── 模块名称:vue-demo模块 //前端vue集成
├── document // 文档
│ └── JavaKnowledgeDocument //java知识点
│ └── java基础知识点.md
│ └── mq知识点.md
│ └── mysql知识点.md
│ └── redis知识点.md
│ └── springcould知识点.md
│ └── spring知识点.md
│ └── FounderDocument //创始人
│ └── 创始人.md
系列文章:快速集成各种微服务相关的技术,帮助大家可以快速集成到自己的项目中,节约开发时间。
提示:系列文章还未全部完成,后续的文章,会慢慢补充进去的。
文章目录
- 创建spring-cloud-security-oauth2-demo项目
- 修改pom.xml
- 创建bootstrap.yml文件
- 修改application.properties文件
- 创建一个空的config.properties文件
- 创建jwt.jks文件
- 修改SpringCloudSecurityOauth2DemoApplication
- 创建数据库表结构
- 创建AuthResourceServerConfig
- 创建AuthServerJdbcTokenStoreConfig.java
- 创建AuthServerJwtTokenStoreConfig
- 创建AuthServerRedisTokenStoreConfig.java
- 创建AuthTokenEnhancer
- 创建JwtTokenStoreConfig
- 创建RedisStoreConfig
- 创建WebSecurityConfig
- 创建SecurityOauth2Controller
- 创建AppUserinfoEntity
- 创建ResultData
- 创建UserInfoFeignService
- 创建JWTAuthenticationEntryPoint.java
- 创建JWTAuthenticationFilter.java
- 创建JWTAuthorizationFilter.java
- 创建UserServiceHystrix
- 创建JwtCAProperties
- 创建AppUserDetailsService
- 创建UserInfoDetails
- 创建JwtTokenUtil
- 验证Spring Cloud Security Oauth2是否工作
- 授权码模式
- 获取授权码
- 授权登录之后会进行回调获取授权码
- 填入授权码code获取获取access_token
- password模式
- 刷新令牌获取access_token
- 发起请求使用access_token进行授权获取登录用户信息
- 创建spring-cloud-security-oauth2-sso-client-demo项目(实现单点登录)
- 修改pom.xml文件
- 创建bootstrap.yml文件
- 修改application.properties文件
- 修改SpringCloudSecurityOauth2SsoClientDemoApplication
- 创建UserController
- 校验请求是否有到授权服务器进行授权
- 授权登录之会进行回调,返回授权码
- 拿到授权码获取access_token
创建spring-cloud-security-oauth2-demo项目
项目代码:https://gitee.com/java_wxid/java_wxid/tree/master/demo/spring-cloud-security-oauth2-demo
项目结构如下(示例):
修改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><groupId>com.example</groupId><artifactId>spring-cloud-security-oauth2-demo</artifactId><version>0.0.1-SNAPSHOT</version><name>spring-cloud-security-oauth2-demo</name><description>Demo project for Spring Boot</description><!-- 属性配置--><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><java.version>1.8</java.version><!--引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件,进行依赖版本的管理,防止不兼容。在 https://dwz.cn/mcLIfNKt 文章中,Spring Cloud Alibaba 开发团队推荐了三者的依赖关系--><spring.boot.version>2.3.12.RELEASE</spring.boot.version><spring.cloud.version>Hoxton.SR12</spring.cloud.version><spring.cloud.alibaba.version>2.2.7.RELEASE</spring.cloud.alibaba.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!--代表web模块,在这个模块中含了许多JAR包,有spring相关的jar,内置tomcat服务器,jackson等,这些web项目中常用的的功能都会自动引入--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- Alibaba Nacos 配置 --><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId></dependency><!-- 在SpringBoot 2.4.x的版本之后,对于bootstrap.properties/bootstrap.yaml配置文件(我们合起来成为Bootstrap配置文件)的支持,其实这个jar包里什么都没有,就只有一个标识类Marker,用来标识要开启Bootstrap配置文件的支持,由于父类用了2.5.6版本需要导入如下的依赖--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-bootstrap</artifactId><version>3.1.0</version></dependency><!-- spring security oauth2--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-oauth2</artifactId></dependency><!-- JWT依赖--><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.1</version></dependency>
<!-- lombok插件--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>RELEASE</version><scope>compile</scope></dependency>
<!-- commons-lang3工具包--><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId><version>3.10</version></dependency><!--openfeign客户端依赖--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-openfeign-core</artifactId><version>2.2.6.RELEASE</version></dependency><!--引入HttpClient依赖--><dependency><groupId>io.github.openfeign</groupId><artifactId>feign-httpclient</artifactId></dependency><!-- 引入Feign Slf4j --><dependency><groupId>com.netflix.feign</groupId><artifactId>feign-slf4j</artifactId><version>8.14.4</version></dependency><!-- jdbc--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><!-- redis --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId></dependency>
<!-- fastjson--><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.62</version></dependency></dependencies><!--引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件,进行依赖版本的管理,防止不兼容。在 https://dwz.cn/mcLIfNKt 文章中,Spring Cloud Alibaba 开发团队推荐了三者的依赖关系--><dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>${spring.boot.version}</version><type>pom</type><scope>import</scope></dependency><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><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>2.4</version><configuration><source>${java.version}</source><target>${java.version}</target><encoding>${project.build.sourceEncoding}</encoding></configuration></plugin></plugins><resources><resource><directory>src/main/resources</directory><filtering>true</filtering><excludes><exclude>**/*.jks</exclude></excludes></resource><resource><directory>src/main/resources</directory><filtering>false</filtering><includes><include>**/*.jks</include></includes></resource></resources></build>
</project>
创建bootstrap.yml文件
代码如下(示例):
#bootstrap.yml优先级比application.yml优先级高
spring:#prefix−{spring.profile.active}.${file-extension}#nacos会根据当前环境去拼接配置名称查找相应配置文件,#示例:{spring.application.name}-{spring.profiles.active}-{spring.cloud.nacos.config.file-extension}#获取到值:nacos-autoconfig-service-dev.ymlprofiles:#开发环境dev,测试环境test,生产环境prodactive: devapplication:#配置应用的名称,用于获取配置name: security-oauth2main:allow-bean-definition-overriding: truecloud:nacos:discovery:# 服务注册地址server-addr: ip:8848config:#nacos配置中心地址server-addr: ip:8848#配置中心的命名空间idnamespace: 9e50b6d9-6c3d-4e7a-b701-10f085e4b98d#配置分组,默认没有也可以group: DEFAULT_GROUP#配置文件后缀,用于拼接配置配置文件名称,目前只支持yaml和propertiesfile-extension: yaml#配置自动刷新refresh-enabled: true#配置文件的前缀,默认是application.name的值,如果配了prefix,就取prefix的值#prefix: nacos-autoconfig-service-${spring.profile.active}# 配置编码encode: UTF-8username: nacospassword: nacosdatasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://ip:3306/oauth2?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2b8username: rootpassword: ca0a997ee4770063hikari:minimum-idle: 5idle-timeout: 600000maximum-pool-size: 10auto-commit: truepool-name: MyHikariCPmax-lifetime: 3000000 #连接最大存活时间,默认值30分钟.设置应该比mysql设置的超时时间短connection-timeout: 30000connection-test-query: SELECT 1 #连接测试查询redis:host: ipdatabase: 0client-name: rootpassword: ca0a997ee4770063
auth:jwt:keyPairName: jwt.jkskeyPairAlias: jwtkeyPairSecret: 123456keyPairStoreSecret: 123456
修改application.properties文件
server.port=8807
创建一个空的config.properties文件
创建jwt.jks文件
# Keytool 是一个java提供的证书管理工具
# -alias:密钥的别名
# -keyalg:使用的hash算法
# -keypass:密钥的访问密码
# -keystore:密钥库文件名,jwt.jks -> 生成的证书
# -storepass:密钥库的访问密码
keytool -genkeypair -alias jwt -keyalg RSA -keypass 123456 -keystore jwt.jks -storepass 123456# 查询证书
keytool -list -keystore jwt.jks
把生成的文件复制到项目的resource目录下面
修改SpringCloudSecurityOauth2DemoApplication
代码如下(示例):
package com.example.springcloudsecurityoauth2demo;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;@EnableDiscoveryClient
@SpringBootApplication
public class SpringCloudSecurityOauth2DemoApplication {public static void main(String[] args) {SpringApplication.run(SpringCloudSecurityOauth2DemoApplication.class, args);}}
创建数据库表结构
代码如下(示例):
#官方的sql: https://github.com/spring-projects/spring-security-oauth/blob/master/spring-security-oauth2/src/test/resources/schema.sqlCREATE TABLE `clientdetails` (`appId` varchar(128) NOT NULL,`resourceIds` varchar(255) DEFAULT NULL,`appSecret` varchar(255) DEFAULT NULL,`scope` varchar(255) DEFAULT NULL,`grantTypes` varchar(255) DEFAULT NULL,`redirectUrl` varchar(255) DEFAULT NULL,`authorities` varchar(255) DEFAULT NULL,`access_token_validity` int(11) DEFAULT NULL,`refresh_token_validity` int(11) DEFAULT NULL,`additionalInformation` varchar(4096) DEFAULT NULL,`autoApproveScopes` varchar(255) DEFAULT NULL,PRIMARY KEY (`appId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;CREATE TABLE `oauth_access_token` (`token_id` varchar(255) DEFAULT NULL,`token` blob,`authentication_id` varchar(128) NOT NULL,`user_name` varchar(255) DEFAULT NULL,`client_id` varchar(255) DEFAULT NULL,`authentication` blob,`refresh_token` varchar(255) DEFAULT NULL,PRIMARY KEY (`authentication_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;CREATE TABLE `oauth_approvals` (`userId` varchar(255) DEFAULT NULL,`clientId` varchar(255) DEFAULT NULL,`scope` varchar(255) DEFAULT NULL,`status` varchar(10) DEFAULT NULL,`expiresAt` timestamp NULL DEFAULT NULL,`lastModifiedAt` timestamp NULL DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;CREATE TABLE `oauth_client_details` (`client_id` varchar(128) NOT NULL,`resource_ids` varchar(255) DEFAULT NULL,`client_secret` varchar(255) DEFAULT NULL,`scope` varchar(255) DEFAULT NULL,`authorized_grant_types` varchar(255) DEFAULT NULL,`web_server_redirect_uri` varchar(255) DEFAULT NULL,`authorities` varchar(255) DEFAULT NULL,`access_token_validity` int(11) DEFAULT NULL,`refresh_token_validity` int(11) DEFAULT NULL,`additional_information` varchar(4096) DEFAULT NULL,`autoapprove` varchar(255) DEFAULT NULL,PRIMARY KEY (`client_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;CREATE TABLE `oauth_client_token` (`token_id` varchar(255) DEFAULT NULL,`token` blob,`authentication_id` varchar(128) NOT NULL,`user_name` varchar(255) DEFAULT NULL,`client_id` varchar(255) DEFAULT NULL,PRIMARY KEY (`authentication_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;CREATE TABLE `oauth_code` (`code` varchar(255) DEFAULT NULL,`authentication` blob
) ENGINE=InnoDB DEFAULT CHARSET=utf8;CREATE TABLE `oauth_refresh_token` (`token_id` varchar(255) DEFAULT NULL,`token` blob,`authentication` blob
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
插入语句
insert into `oauth_client_details` (`client_id`, `resource_ids`, `client_secret`, `scope`, `authorized_grant_types`, `web_server_redirect_uri`, `authorities`, `access_token_validity`, `refresh_token_validity`, `additional_information`, `autoapprove`) values('client','auth','$2a$10$dis6W.9y3lqGKHCM89WCa.XiE4zkGND63TdKzLtR/jnSJvTsoOcy.','All','authorization_code,password,refresh_token,implicit','http://localhost:8807/securityOauth2/getCodeByCallback',NULL,NULL,NULL,NULL,'true');
如下图(示例):
创建AuthResourceServerConfig
代码如下(示例):
package com.example.springcloudsecurityoauth2demo.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;/*** @Author: liaozhiwei* @Description: 资源服务配置* @Date: Created in 18:19 2022/8/23*/
@Configuration
@EnableResourceServer
public class AuthResourceServerConfig extends ResourceServerConfigurerAdapter {@Overridepublic void configure(HttpSecurity http) throws Exception {//利用RequestMatcher对象来进行路径匹配了,调用了antMatchers方法来定义什么样的请求可以放过,什么样的请求需要验证http.authorizeRequests().anyRequest().authenticated().and().requestMatchers().antMatchers("/user/**");}
}
创建AuthServerJdbcTokenStoreConfig.java
这个是基于DB模式配置授权服务器存储第三方客户端的信息,由于DB/Jwt/Redis三种方式选择其中一个进行存储,目前测试过可行,所以注释了
代码如下(示例):
//package com.example.springcloudsecurityoauth2demo.config;
//
//import com.example.springcloudsecurityoauth2demo.service.AppUserDetailsService;
//import org.springframework.beans.factory.annotation.Autowired;
//import org.springframework.context.annotation.Bean;
//import org.springframework.context.annotation.Configuration;
//import org.springframework.http.HttpMethod;
//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.TokenEnhancer;
//import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
//import org.springframework.security.oauth2.provider.token.store.JdbcTokenStore;
//import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
//import javax.sql.DataSource;
//import java.util.ArrayList;
//import java.util.List;
//
///**
// * @Author: liaozhiwei
// * @Description: 第一种方式:基于DB模式配置授权服务器存储第三方客户端的信息
// * @Date: Created in 18:17 2022/8/23
// */
//@Configuration
//@EnableAuthorizationServer
//public class AuthServerJdbcTokenStoreConfig extends AuthorizationServerConfigurerAdapter {//
// //第一种方式:使用密码模式需要配置(使用jdbc的方式)
// @Autowired
// private DataSource dataSource;
// @Bean
// public JdbcTokenStore jdbcTokenStore(){// return new JdbcTokenStore(dataSource);
// }
// @Bean
// public ClientDetailsService clientDetailsService(){// return new JdbcClientDetailsService(dataSource);
// }
//
// @Autowired
// private JwtAccessTokenConverter jwtAccessTokenConverter;
//
// @Autowired
// private AppUserDetailsService userDetailService;
//
// @Autowired
// private AuthenticationManager authenticationManagerBean;
//
// @Autowired
// private AuthTokenEnhancer authTokenEnhancer;
//
// /**
// * @Description 第三方信息的存储
// **/
// @Override
// public void configure(ClientDetailsServiceConfigurer clients) throws Exception {// /**
// * 第一步
// *授权码模式(安全级别最高)
// *http://localhost:8807/oauth/authorize?response_type=code&client_id=client&redirect_uri=http://www.baidu.com&scope=all
// *或者
// *http://localhost:8807/oauth/authorize?response_type=code&client_id=client
// *password模式
// *http://localhost:8807/oauth/token?username=liaozhiwei&password=123456&grant_type=password&client_id=client&client_secret=client_secret&scope=all
// *刷新令牌
// *http://localhost:8080/oauth/token?grant_type=refresh_token&client_id=client&client_secret=client_secret&refresh_token=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJsaWFvemhpd2VpIiwic2NvcGUiOlsiYWxsIl0sImF0aSI6IjJiYjg5NDY5LWIyNmYtNGQwNC05YTZjLTJjYmZkYjIzMDgyNyIsImV4cCI6MTY2MjE3ODkzMiwiYXV0aG9yaXRpZXMiOlsiYWRtaW4iXSwianRpIjoiNzBmOTc0NWItNGZmOS00ZTY5LThiOWMtNzY5ZGNkODc5NTcwIiwiY2xpZW50X2lkIjoiY2xpZW50In0.Uy6s5uIT2h_vv6DezssqzA1d7iNpqyfNReiAygmYrapPuc1Beyetoxbf3_zduD8BhZkKva7Qna_L9lFQKZuzzSx25RLgG07YzDZnuUPkCVYisxZ4bhmOuJYndKzrZmZBqmK2P9MQwLhasEgcpZoR5RJurV15fZOO5IvOI6xvgM0XkqalnEwYWf5e5JYLEEBqQqTpkEoP6wH3SSBsRFuH10l6qKqUXFd_nO37hO1p-d2uX-qMBTGPZ57xiaz97x5FLGxh2dbskmxyTnf-jAiTHlRdrfvIHDh312uW4iyENZpg8HEg3OjUHYc-7OY4U9UrHQx0YQbJ01SuKLCFEIx-aA
// */
// /**
// * 第二步
// * 获取access_token
// * http://localhost:8807/oauth/token?grant_type=authorization_code&client_id=client&client_secret=client_secret&code=07Sn4f
// */
// // 第三方信息的存储 基于jdbc
// clients.withClientDetails(clientDetailsService());
// }
//
// @Override
// public void configure(AuthorizationServerEndpointsConfigurer endpoints) {// //配置JWT的内容增强器
// TokenEnhancerChain enhancerChain = new TokenEnhancerChain();
// List<TokenEnhancer> delegates = new ArrayList<>();
// delegates.add(authTokenEnhancer);
// delegates.add(jwtAccessTokenConverter);
// enhancerChain.setTokenEnhancers(delegates);
//
// //第一种:使用密码模式需要配置(使用jdbc的方式)
// endpoints.authenticationManager(authenticationManagerBean)
// .reuseRefreshTokens(false) //refresh_token是否重复使用
// .userDetailsService(userDetailService) //刷新令牌授权包含对用户信息的检查
// .tokenStore(new JdbcTokenStore(dataSource)) //指定token存储策略是jwt,存储到mysql
// .accessTokenConverter(jwtAccessTokenConverter)
// .tokenEnhancer(enhancerChain) //配置tokenEnhancer
// .allowedTokenEndpointRequestMethods(HttpMethod.GET,HttpMethod.POST,HttpMethod.DELETE,HttpMethod.PUT); //支持GET,POST请求,DELETE请求,PUT请求
// }
//
// /**
// * 授权服务器安全配置
// * @param security
// * @throws Exception
// */
// @Override
// public void configure(AuthorizationServerSecurityConfigurer security) {// //第三方客户端校验token需要带入 clientId 和clientSecret来校验
// security.checkTokenAccess("isAuthenticated()")
// .tokenKeyAccess("isAuthenticated()");//来获取我们的tokenKey需要带入clientId,clientSecret
// //允许表单认证
// security.allowFormAuthenticationForClients();
// }
//}
//
创建AuthServerJwtTokenStoreConfig
这个是基于jwt.jks文件授权服务器存储第三方客户端的信息,由于DB/Jwt/Redis三种方式选择其中一个进行存储,目前测试过可行
代码如下(示例):
package com.example.springcloudsecurityoauth2demo.config;import com.example.springcloudsecurityoauth2demo.service.AppUserDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.password.PasswordEncoder;
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.token.TokenEnhancer;
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import java.util.ArrayList;
import java.util.List;/*** @Author: liaozhiwei* @Description: 第二种方式:基于jwt.jks文件授权服务器存储第三方客户端的信息* @Date: Created in 18:17 2022/8/23*/
@Configuration
@EnableAuthorizationServer
public class AuthServerJwtTokenStoreConfig extends AuthorizationServerConfigurerAdapter {//第二种方式:使用密码模式需要配置(使用jwt文件的方式)@Autowired@Qualifier("jwtTokenStore")private TokenStore jwtTokenStore;@Autowiredprivate JwtAccessTokenConverter jwtAccessTokenConverter;@Autowiredprivate AppUserDetailsService userDetailService;@Autowiredprivate AuthenticationManager authenticationManagerBean;@Autowiredprivate AuthTokenEnhancer authTokenEnhancer;@Autowiredprivate PasswordEncoder passwordEncoder;/*** @Description 第三方信息的存储**/@Overridepublic void configure(ClientDetailsServiceConfigurer clients) throws Exception {/*** 第一种方式:授权码模式(安全级别最高)获取access_token* 第一步* http://127.0.0.1:8807/oauth/authorize?response_type=code&client_id=client&redirect_uri=http://www.baidu.com&scope=all*或者* http://127.0.0.1:8807/oauth/authorize?response_type=code&client_id=client* 第二步* 获取access_token* http://127.0.0.1:8807/oauth/token?grant_type=authorization_code&client_id=client&client_secret=client_secret&code=JQFXW7* 第二种方式:password模式获取access_token* http://127.0.0.1:8807/oauth/token?username=liaozhiwei&password=123456&grant_type=password&client_id=client&client_secret=client_secret&scope=all* 刷新令牌获取access_token*http://127.0.0.1:8807/oauth/token?grant_type=refresh_token&client_id=client&client_secret=client_secret&refresh_token= *//*** 其他服务发起请求使用access_token进行授权* http://127.0.0.1:8808/securityOauth2/getUserByTokenStore?access_token=* http://127.0.0.1:8808/securityOauth2/getUserByAuthentication?access_token=* http://127.0.0.1:8808/securityOauth2/getUserByRequest?access_token=* */clients.inMemory()//配置client_id.withClient("client")//配置client-secret.secret(passwordEncoder.encode("client_secret"))//配置访问token的有效期.accessTokenValiditySeconds(3600)//配置刷新token的有效期.refreshTokenValiditySeconds(864000)//配置redirect_uri,用于授权成功后跳转,可以配置多个,例如:.redirectUris("http://localhost:8081/login","http://localhost:8082/login").redirectUris("http://127.0.0.1:8807/securityOauth2/redirectUris")//自动授权配置.autoApprove(true)//配置申请的权限范围.scopes("all")/*** 配置grant_type,表示授权类型* authorization_code: 授权码* password: 密码* client_credentials: 客户端* refresh_token: 更新令牌*/.authorizedGrantTypes("authorization_code","password","refresh_token");}@Overridepublic void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {//配置JWT的内容增强器TokenEnhancerChain enhancerChain = new TokenEnhancerChain();List<TokenEnhancer> delegates = new ArrayList<>();delegates.add(authTokenEnhancer);delegates.add(jwtAccessTokenConverter);enhancerChain.setTokenEnhancers(delegates);//第二种:使用密码模式需要配置(使用jwt文件的方式)endpoints.authenticationManager(authenticationManagerBean) //使用密码模式需要配置.reuseRefreshTokens(false) //refresh_token是否重复使用.userDetailsService(userDetailService) //刷新令牌授权包含对用户信息的检查.tokenStore(jwtTokenStore) //配置存储令牌策略(使用jwt文件存储的方式).accessTokenConverter(jwtAccessTokenConverter).tokenEnhancer(enhancerChain) //配置tokenEnhancer.allowedTokenEndpointRequestMethods(HttpMethod.GET,HttpMethod.POST,HttpMethod.DELETE,HttpMethod.PUT); //支持GET,POST请求,DELETE请求,PUT请求}/*** 授权服务器安全配置* @param security* @throws Exception*/@Overridepublic void configure(AuthorizationServerSecurityConfigurer security) throws Exception {// 允许客户表单认证,不加的话/oauth/token无法访问security.allowFormAuthenticationForClients()// 对于CheckEndpoint控制器[框架自带的校验]的/oauth/token端点允许所有客户端发送器请求而不会被Spring-security拦截// 开启/oauth/token_key验证端口无权限访问.tokenKeyAccess("permitAll()")// 要访问/oauth/check_token必须设置为permitAll(),但这样所有人都可以访问了,设为isAuthenticated()又导致访问不了,这个问题暂时没找到解决方案// 开启/oauth/check_token验证端口认证权限访问.checkTokenAccess("permitAll()")
// //第三方客户端校验token需要带入 clientId 和clientSecret来校验.checkTokenAccess("isAuthenticated()").tokenKeyAccess("isAuthenticated()");//来获取我们的tokenKey需要带入clientId,clientSecret//允许客户表单认证,不加的话/oauth/token无法访问security.allowFormAuthenticationForClients();}}
创建AuthServerRedisTokenStoreConfig.java
这个是基于Redis模式配置授权服务器存储第三方客户端的信息,由于DB/Jwt/Redis三种方式选择其中一个进行存储,目前测试过可行,所以注释掉了。
代码如下(示例):
//package com.example.springcloudsecurityoauth2demo.config;
//
//import org.springframework.beans.factory.annotation.Qualifier;
//import org.springframework.security.crypto.password.PasswordEncoder;
//import org.springframework.security.oauth2.provider.token.TokenStore;
//import com.example.springcloudsecurityoauth2demo.service.AppUserDetailsService;
//import org.springframework.beans.factory.annotation.Autowired;
//import org.springframework.context.annotation.Configuration;
//import org.springframework.http.HttpMethod;
//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.token.TokenEnhancer;
//import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
//import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
//import java.util.ArrayList;
//import java.util.List;
//
///**
// * @Author: liaozhiwei
// * @Description: 第三种方式:基于Redis模式配置授权服务器存储第三方客户端的信息
// * @Date: Created in 18:17 2022/8/23
// */
//@Configuration
//@EnableAuthorizationServer
//public class AuthServerRedisTokenStoreConfig extends AuthorizationServerConfigurerAdapter {//
// //使用密码模式需要配置(使用redis的方式)
// @Autowired
// @Qualifier("redisTokenStore")
// private TokenStore redisTokenStore;
//
// @Autowired
// private JwtAccessTokenConverter jwtAccessTokenConverter;
//
// @Autowired
// private AppUserDetailsService userDetailService;
//
// @Autowired
// private AuthenticationManager authenticationManagerBean;
//
// @Autowired
// private AuthTokenEnhancer authTokenEnhancer;
//
// @Autowired
// private PasswordEncoder passwordEncoder;
//
// /**
// * @Description 第三方信息的存储
// **/
// @Override
// public void configure(ClientDetailsServiceConfigurer clients) throws Exception {// /**
// *授权码模式
// *http://localhost:8807/oauth/authorize?response_type=code&client_id=client&redirect_uri=http://www.baidu.com&scope=all
// *或者
// *http://localhost:8807/oauth/authorize?response_type=code&client_id=client
// *password模式
// *http://localhost:8807/oauth/token?username=liaozhiwei&password=123456&grant_type=password&client_id=client&client_secret=client-secret&scope=all
// *刷新令牌
// *http://localhost:8080/oauth/token?grant_type=refresh_token&client_id=client&client_secret=client-secret&refresh_token=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJsaWFvemhpd2VpIiwic2NvcGUiOlsiYWxsIl0sImF0aSI6IjJiYjg5NDY5LWIyNmYtNGQwNC05YTZjLTJjYmZkYjIzMDgyNyIsImV4cCI6MTY2MjE3ODkzMiwiYXV0aG9yaXRpZXMiOlsiYWRtaW4iXSwianRpIjoiNzBmOTc0NWItNGZmOS00ZTY5LThiOWMtNzY5ZGNkODc5NTcwIiwiY2xpZW50X2lkIjoiY2xpZW50In0.Uy6s5uIT2h_vv6DezssqzA1d7iNpqyfNReiAygmYrapPuc1Beyetoxbf3_zduD8BhZkKva7Qna_L9lFQKZuzzSx25RLgG07YzDZnuUPkCVYisxZ4bhmOuJYndKzrZmZBqmK2P9MQwLhasEgcpZoR5RJurV15fZOO5IvOI6xvgM0XkqalnEwYWf5e5JYLEEBqQqTpkEoP6wH3SSBsRFuH10l6qKqUXFd_nO37hO1p-d2uX-qMBTGPZ57xiaz97x5FLGxh2dbskmxyTnf-jAiTHlRdrfvIHDh312uW4iyENZpg8HEg3OjUHYc-7OY4U9UrHQx0YQbJ01SuKLCFEIx-aA
// */
//
// clients.inMemory()
// //配置client_id
// .withClient("client")
// //配置client-secret
// .secret(passwordEncoder.encode("client-secret"))
// //配置访问token的有效期
// .accessTokenValiditySeconds(3600)
// //配置刷新token的有效期
// .refreshTokenValiditySeconds(864000)
// //配置redirect_uri,用于授权成功后跳转,可以配置多个,例如:.redirectUris("http://localhost:8081/login","http://localhost:8082/login")
// .redirectUris("http://www.baidu.com")
// //配置申请的权限范围
// .scopes("all")
// //自动授权配置
// .autoApprove(true)
// /**
// * 配置grant_type,表示授权类型
// * authorization_code: 授权码
// * password: 密码
// * client_credentials: 客户端
// * refresh_token: 更新令牌
// * implicit:简化模式
// */
// .authorizedGrantTypes("authorization_code","password","refresh_token");
// }
//
// @Override
// public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {// //配置JWT的内容增强器
// TokenEnhancerChain enhancerChain = new TokenEnhancerChain();
// List<TokenEnhancer> delegates = new ArrayList<>();
// delegates.add(authTokenEnhancer);
// delegates.add(jwtAccessTokenConverter);
// enhancerChain.setTokenEnhancers(delegates);
//
// //使用密码模式需要配置(使用redis的方式)
// endpoints.authenticationManager(authenticationManagerBean) //使用密码模式需要配置
// .reuseRefreshTokens(false) //refresh_token是否重复使用
// .userDetailsService(userDetailService) //刷新令牌授权包含对用户信息的检查
// .tokenStore(redisTokenStore) //指定token存储到redis
// .accessTokenConverter(jwtAccessTokenConverter)
// .allowedTokenEndpointRequestMethods(HttpMethod.GET,HttpMethod.POST,HttpMethod.DELETE,HttpMethod.PUT); //支持GET,POST请求,DELETE请求,PUT请求
// }
//
// /**
// * 授权服务器安全配置
// * @param security
// * @throws Exception
// */
// @Override
// public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {// //第三方客户端校验token需要带入 clientId 和clientSecret来校验
// security.checkTokenAccess("isAuthenticated()")
// .tokenKeyAccess("isAuthenticated()");//来获取我们的tokenKey需要带入clientId,clientSecret
// //允许表单认证
// security.allowFormAuthenticationForClients();
// }
//
//
//}
//
创建AuthTokenEnhancer
代码如下(示例):
package com.example.springcloudsecurityoauth2demo.config;import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import java.util.HashMap;
import java.util.Map;/*** @Author: liaozhiwei* @Description: TODO* @Date: Created in 18:20 2022/8/23*/
public class AuthTokenEnhancer implements TokenEnhancer {@Overridepublic OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {UserDetails userinfoDetails = (UserDetails) authentication.getPrincipal();final Map<String, Object> additionalInfo = new HashMap<>();final Map<String, Object> retMap = new HashMap<>();//todo 这里暴露userId到Jwt的令牌中,后期可以根据自己的业务需要 进行添加字段additionalInfo.put("userName",userinfoDetails.getUsername());retMap.put("additionalInfo",additionalInfo);((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(retMap);return accessToken;}
}
创建JwtTokenStoreConfig
代码如下(示例):
package com.example.springcloudsecurityoauth2demo.config;import com.example.springcloudsecurityoauth2demo.properties.JwtCAProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.rsa.crypto.KeyStoreKeyFactory;
import java.security.KeyPair;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;/*** @Author: liaozhiwei* @Description: jwt.jks文件形式存储* @Date: Created in 19:09 2022/8/23*/@Configuration
@EnableConfigurationProperties(value = JwtCAProperties.class)
public class JwtTokenStoreConfig {//第二种方式:使用jwt文件的方式@Beanpublic TokenStore jwtTokenStore(){return new JwtTokenStore(jwtAccessTokenConverter());}@Beanpublic AuthTokenEnhancer authTokenEnhancer() {return new AuthTokenEnhancer();}@Autowiredprivate JwtCAProperties jwtCAProperties;@Beanpublic KeyPair keyPair() {KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(new ClassPathResource(jwtCAProperties.getKeyPairName()),jwtCAProperties.getKeyPairSecret().toCharArray());return keyStoreKeyFactory.getKeyPair(jwtCAProperties.getKeyPairAlias(),jwtCAProperties.getKeyPairStoreSecret().toCharArray());}@Beanpublic JwtAccessTokenConverter jwtAccessTokenConverter(){JwtAccessTokenConverter accessTokenConverter = new JwtAccessTokenConverter();//配置JWT使用的秘钥 非对称加密accessTokenConverter.setKeyPair(keyPair());return accessTokenConverter;}}
创建RedisStoreConfig
代码如下(示例):
package com.example.springcloudsecurityoauth2demo.config;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;/*** @Author: liaozhiwei* @Description: 第三种方式:redis存储* @Date: Created in 19:09 2022/8/23*/
@Configuration
public class RedisStoreConfig {@Autowiredprivate RedisConnectionFactory redisConnectionFactory;//第三种:使用密码模式需要配置(使用redis的方式)
// @Bean
// public TokenStore redisTokenStore(){// // access_token
// return new RedisTokenStore(redisConnectionFactory);
// }
}
创建WebSecurityConfig
代码如下(示例):
package com.example.springcloudsecurityoauth2demo.config;//import com.example.springcloudsecurityoauth2demo.filter.JWTAuthenticationEntryPoint;
//import com.example.springcloudsecurityoauth2demo.filter.JWTAuthenticationFilter;
//import com.example.springcloudsecurityoauth2demo.filter.JWTAuthorizationFilter;
import com.example.springcloudsecurityoauth2demo.service.AppUserDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
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.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;/*** @Author: liaozhiwei* @Description: 配置SpringSecurity,“将Spring Security与Spring Gateway一起使用时出现无法访问javax.servlet.Filter”错误”,* * 把Spring Gateway和Spring Security放在一起,因为我想保护我的网关。但是在实现了以下扩展WebSecurityConfigurerAdapter的类之后,* * 项目抛出java:无法访问javax.servlet.Filter* * 从Spring Cloud Gateway文档中:Spring Cloud Gateway需要Spring Boot和Spring Webflux提供的Netty运行时。* * 它不能在传统的Servlet容器中工作,也不能在构建为WAR时工作。扩展WebSecurityConfigurerAdapter是为了基于servlet的应用程序* @Date: Created in 18:11 2022/8/23*/
@EnableWebSecurity
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {@Autowiredprivate AppUserDetailsService userDetailService;@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.userDetailsService(userDetailService);}@Bean@Overridepublic AuthenticationManager authenticationManagerBean() throws Exception {// oauth2 密码模式需要拿到这个beanreturn super.authenticationManagerBean();}/*** @Description 密码模式* @MethodReturnType org.springframework.security.crypto.password.PasswordEncoder* @Author zhiwei Liao* @Date 2021/8/17 15:46**/@Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}@Overrideprotected void configure(HttpSecurity http) throws Exception {//第一种方式http.formLogin().permitAll().and().authorizeRequests().antMatchers("/oauth/**").permitAll()//不拦截.anyRequest().authenticated().and().logout().permitAll()//退出放行.and().csrf().disable();//关闭CSRF保护//第二种方式
/* http.cors().and().authorizeRequests().antMatchers("/user/**").hasRole("admin").anyRequest().permitAll()// 添加JWT登录拦截器.and().addFilter(new JWTAuthenticationFilter(authenticationManager()))// 添加JWT鉴权拦截器.addFilter(new JWTAuthorizationFilter(authenticationManager()))// 设置Session的创建策略为:Spring Security不创建HttpSession.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)// 匿名用户访问无权限资源时的异常处理.and().exceptionHandling().authenticationEntryPoint(new JWTAuthenticationEntryPoint()).and().csrf().disable();//关闭CSRF保护*/}@BeanCorsConfigurationSource corsConfigurationSource(){final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();// 注册跨域配置source.registerCorsConfiguration("/**", new CorsConfiguration().applyPermitDefaultValues());return source;}
}
创建SecurityOauth2Controller
代码如下(示例):
package com.example.springcloudsecurityoauth2demo.controller;import io.jsonwebtoken.Jwts;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;import javax.servlet.http.HttpServletRequest;
import java.nio.charset.StandardCharsets;/*** @Author: liaozhiwei* @Description: 获取当前登录用户信息* @Date: Created in 09:24 2022/8/24*/
@RestController
@RequestMapping("/securityOauth2")
public class SecurityOauth2Controller {/*** 根据重定向的返回地址获取授权码* @return*/@RequestMapping("/getCodeByCallback")public Object getCodeByCallback() {return "这是一个回调方法(授权码模式:A网站提供一个链接,用户点击后就会跳转到B网站,用户跳转后,B网站会要求用户登录,然后询问是否同意给予A网站授权,用户表示同意,这时B网站就会跳回redirect_uri参数指定的网址,也就是当前这个接口,跳转时,会传回一个授权码";}/*** 回调方法* @return*/@RequestMapping("/redirectUris")public Object redirectUris(HttpServletRequest request) {return "这是一个回调方法,第三方应用授权登录之后,回调到这里,我们可以进行自己系统登录业务,将用户的登录信息录入";}@Autowiredprivate TokenStore tokenService;/*** 获取当前登录用户信息* @param token* @return*/@RequestMapping("/getUserByTokenStore")public Object getUserByTokenStore(@RequestParam("access_token") String token) {OAuth2Authentication oAuth2Authentication = tokenService.readAuthentication(token);return oAuth2Authentication.getUserAuthentication().getPrincipal();}/*** 获取当前登录用户信息* @param request* @return*/@GetMapping("/getUserByRequest")public Object getUserByRequest(HttpServletRequest request) {String header = request.getHeader("Authorization");String token;if(header!=null){token = header.substring(header.indexOf("bearer") + 7);}else {token = request.getParameter("access_token");}return Jwts.parser().setSigningKey("123123".getBytes(StandardCharsets.UTF_8)).parseClaimsJws(token).getBody();}}
创建AppUserinfoEntity
代码如下(示例):
package com.example.springcloudsecurityoauth2demo.entity;import lombok.Getter;
import lombok.Setter;/*** @Author: liaozhiwei* @Description: 用户的实体类* @Date: Created in 18:24 2022/8/23*/
@Setter
@Getter
public class AppUserinfoEntity {private String userId;private String userName;private String userStatus;private String credential;
}
创建ResultData
代码如下(示例):
package com.example.springcloudsecurityoauth2demo.entity.base;/*** @Author: liaozhiwei* @Description: TODO* @Date: Created in 18:34 2022/8/23*/import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;@Data
@NoArgsConstructor
public class ResultData<T> implements Serializable {/*** 状态码*/public boolean status = true;/*** 状态码*/private Integer code = 200;/*** 接口返回信息*/private String msg;/*** 数据对象*/private T data;/*** 初始化一个新创建的 ResultData 对象** @param status 状态码* @param msg 返回内容*/public ResultData(Boolean status, String msg) {this.status = status;this.msg = msg;}/*** 初始化一个新创建的 ResultData 对象** @param status 状态码* @param msg 返回内容* @param data 数据对象*/public ResultData(Boolean status, String msg, T data, Integer code) {this.status = status;this.msg = msg;this.data = data;this.code = code;}public ResultData(T data) {this.data = data;}/*** 返回成功消息** @param msg 返回内容* @param data 数据对象* @return 成功消息*/public static <T> ResultData<T> success(String msg, T data) {return new ResultData<T>(true, msg, data, 200);}/*** 返回成功消息** @param msg 返回内容* @return 成功消息*/public static <T> ResultData<T> success(String msg) {return ResultData.success(msg, null);}/*** 返回成功消息** @return 成功消息*/public static <T> ResultData<T> success() {return ResultData.success(null);}/*** 返回成功数据** @return 成功消息*/public static <T> ResultData<T> success(T data) {return ResultData.success(null, data);}/*** 返回错误消息** @return*/public static <T> ResultData<T> error() {return ResultData.error(null);}/*** 返回错误消息** @param msg 返回内容* @return 警告消息*/public static <T> ResultData<T> error(String msg) {return ResultData.error(msg, null);}/*** 返回错误消息** @param code 状态码* @param msg 返回内容* @return 警告消息*/public static <T> ResultData<T> error(Integer code, String msg) {return new ResultData<T>(false, msg, null, code);}/*** 返回错误消息** @param msg 返回内容* @param data 数据对象* @return 警告消息*/public static <T> ResultData<T> error(String msg, T data) {return new ResultData<T>(false, msg, data, 500);}
}
创建UserInfoFeignService
代码如下(示例):
package com.example.springcloudsecurityoauth2demo.fegin;import com.example.springcloudsecurityoauth2demo.entity.AppUserinfoEntity;
import com.example.springcloudsecurityoauth2demo.entity.base.ResultData;
import com.example.springcloudsecurityoauth2demo.hystrix.UserServiceHystrix;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;/*** @Author: liaozhiwei* @Description: TODO 远程调用用户模块的接口获取用户信息,这里就不写用户模块了* @Date: Created in 18:33 2022/8/23*/
@Component
@FeignClient(name = "user", fallback = UserServiceHystrix.class, path = "/user")
public interface UserInfoFeignService {@GetMapping("/getUserinfoById")ResultData<AppUserinfoEntity> getUserinfoById(@RequestParam("userId") String userId);@GetMapping("/getUserByUserName")ResultData<AppUserinfoEntity> getUserByUserName(@RequestParam("username") String username);}
创建JWTAuthenticationEntryPoint.java
代码如下(示例):
//package com.example.springcloudsecurityoauth2demo.filter;
//
//import com.alibaba.fastjson.JSON;
//import org.springframework.security.core.AuthenticationException;
//import org.springframework.security.web.AuthenticationEntryPoint;
//import javax.servlet.ServletException;
//import javax.servlet.http.HttpServletRequest;
//import javax.servlet.http.HttpServletResponse;
//import java.io.IOException;
//
//
///**
// * @Author: liaozhiwei
// * @Description: 匿名用户访问资源时无权限的处理
// * @Date: Created in 11:13 2022/8/24
// */
//
//public class JWTAuthenticationEntryPoint implements AuthenticationEntryPoint {//
// @Override
// public void commence(HttpServletRequest request, HttpServletResponse response,
// AuthenticationException authException) throws IOException, ServletException {// response.setCharacterEncoding("utf-8");
// response.setContentType("text/javascript;charset=utf-8");
// response.getWriter().print(JSON.toJSONString("未登录,没有访问权限"));
// }
//}
创建JWTAuthenticationFilter.java
代码如下(示例):
//package com.example.springcloudsecurityoauth2demo.filter;
//
//import com.alibaba.fastjson.JSON;
//import com.example.springcloudsecurityoauth2demo.utils.JwtTokenUtil;
//import org.springframework.security.authentication.*;
//import org.springframework.security.core.Authentication;
//import org.springframework.security.core.AuthenticationException;
//import org.springframework.security.core.GrantedAuthority;
//import org.springframework.security.core.userdetails.User;
//import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
//import javax.servlet.FilterChain;
//import javax.servlet.ServletException;
//import javax.servlet.http.HttpServletRequest;
//import javax.servlet.http.HttpServletResponse;
//import java.io.IOException;
//import java.util.Collection;
//
///**
// * @Author: liaozhiwei
// * @Description: TODO
// * @Date: Created in 16:13 2022/8/24
// */
//
//public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter {//
// private AuthenticationManager authenticationManager;
//
// public JWTAuthenticationFilter(AuthenticationManager authenticationManager) {// this.authenticationManager = authenticationManager;
// }
//
// @Override
// public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {// UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
// request.getParameter("username"), request.getParameter("password"));
// return authenticationManager.authenticate(token);
// }
//
// @Override
// protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {// User user = (User) authResult.getPrincipal();
// // 从User中获取权限信息
// Collection<? extends GrantedAuthority> authorities = user.getAuthorities();
// // 创建Token
// String token = JwtTokenUtil.createToken(user.getUsername(), authorities.toString());
// response.setCharacterEncoding("UTF-8");
// response.setContentType("application/json; charset=utf-8");
// // 在请求头里返回创建成功的token
// // 设置请求头为带有"Bearer "前缀的token字符串
// response.setHeader("token", JwtTokenUtil.TOKEN_PREFIX + token);
// response.setContentType("text/json;charset=utf-8");
// response.getWriter().write(JSON.toJSONString("登录成功"));
// }
//
// @Override
// protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {// String returnData="";
// if (failed instanceof AccountExpiredException) {// returnData="账号过期";
// }else if (failed instanceof BadCredentialsException) {// returnData="密码错误";
// }else if (failed instanceof CredentialsExpiredException) {// returnData="密码过期";
// }else if (failed instanceof DisabledException) {// returnData="账号不可用";
// }else if (failed instanceof LockedException) {// returnData="账号锁定";
// }else if (failed instanceof InternalAuthenticationServiceException) {// returnData="用户不存在";
// }else{// returnData="未知异常";
// }
//
// response.setContentType("text/json;charset=utf-8");
// response.getWriter().write(JSON.toJSONString(returnData));
// }
//}
创建JWTAuthorizationFilter.java
代码如下(示例):
//package com.example.springcloudsecurityoauth2demo.filter;
//
//import com.example.springcloudsecurityoauth2demo.utils.JwtTokenUtil;
//import org.apache.commons.lang3.StringUtils;
//import org.springframework.security.authentication.AuthenticationManager;
//import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
//import org.springframework.security.core.authority.SimpleGrantedAuthority;
//import org.springframework.security.core.context.SecurityContextHolder;
//import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
//import javax.servlet.FilterChain;
//import javax.servlet.ServletException;
//import javax.servlet.http.HttpServletRequest;
//import javax.servlet.http.HttpServletResponse;
//import java.io.IOException;
//import java.util.ArrayList;
//import java.util.Collection;
//
///**
// * @Author: liaozhiwei
// * @Description: TODO
// * @Date: Created in 16:13 2022/8/24
// */
//
//public class JWTAuthorizationFilter extends BasicAuthenticationFilter {//
//
// public JWTAuthorizationFilter(AuthenticationManager authenticationManager) {// super(authenticationManager);
// }
//
// @Override
// protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {// String tokenHeader = request.getHeader(JwtTokenUtil.TOKEN_HEADER);
// // 若请求头中没有Authorization信息 或是Authorization不以Bearer开头 则直接放行
// if (tokenHeader == null || !tokenHeader.startsWith(JwtTokenUtil.TOKEN_PREFIX)){// filterChain.doFilter(request, response);
// return;
// }
// // 若请求头中有token 则调用下面的方法进行解析 并设置认证信息
// SecurityContextHolder.getContext().setAuthentication(getAuthentication(tokenHeader));
// super.doFilterInternal(request, response, filterChain);
// }
//
// /*
// * 从token中获取用户信息并新建一个token
// * @param tokenHeader 字符串形式的Token请求头
// * @return 带用户名和密码以及权限的Authentication
// */
// private UsernamePasswordAuthenticationToken getAuthentication(String tokenHeader) {// // 去掉前缀 获取Token字符串
// String token = tokenHeader.replace(JwtTokenUtil.TOKEN_PREFIX, "");
// // 从Token中解密获取用户名
// String username = JwtTokenUtil.getUsername(token);
// // 从Token中解密获取用户角色
// String role = JwtTokenUtil.getUserRole(token);
// // 将[ROLE_XXX,ROLE_YYY]格式的角色字符串转换为数组
// String[] roles = StringUtils.strip(role, "[]").split(", ");
// Collection<SimpleGrantedAuthority> authorities=new ArrayList<>();
// for (String s:roles)
// {// authorities.add(new SimpleGrantedAuthority(s));
// }
// if (username != null)
// {// return new UsernamePasswordAuthenticationToken(username, null,authorities);
// }
// return null;
// }
//}
创建UserServiceHystrix
代码如下(示例):
package com.example.springcloudsecurityoauth2demo.hystrix;/*** @Author: liaozhiwei* @Description: TODO* @Date: Created in 18:37 2022/8/23*/
import com.example.springcloudsecurityoauth2demo.entity.AppUserinfoEntity;
import com.example.springcloudsecurityoauth2demo.entity.base.ResultData;
import com.example.springcloudsecurityoauth2demo.fegin.UserInfoFeignService;
import org.springframework.stereotype.Component;/*** @author zhiwei Liao* @version 1.0* @Description* @Date 2021/8/17 15:25*/@Component
public class UserServiceHystrix implements UserInfoFeignService {@Overridepublic ResultData<AppUserinfoEntity> getUserinfoById(String userId) {return null;}@Overridepublic ResultData<AppUserinfoEntity> getUserByUserName(String username) {return null;}
}
创建JwtCAProperties
代码如下(示例):
package com.example.springcloudsecurityoauth2demo.properties;import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
/*** @Author: liaozhiwei* @Description: 读取配置文件中的属性配置* @Date: Created in 19:10 2022/8/23*/@Data
@ConfigurationProperties(prefix = "auth.jwt")
public class JwtCAProperties {/*** 证书名称*/private String keyPairName;/*** 证书别名*/private String keyPairAlias;/*** 证书私钥*/private String keyPairSecret;/*** 证书存储密钥*/private String keyPairStoreSecret;}
创建AppUserDetailsService
代码如下(示例):
package com.example.springcloudsecurityoauth2demo.service;import com.example.springcloudsecurityoauth2demo.entity.AppUserinfoEntity;
import com.example.springcloudsecurityoauth2demo.entity.base.ResultData;
import com.example.springcloudsecurityoauth2demo.fegin.UserInfoFeignService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.security.core.authority.AuthorityUtils;
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;/*** @Author: liaozhiwei* @Description: TODO* @Date: Created in 18:13 2022/8/23*/
@Service
@Slf4j
public class AppUserDetailsService implements UserDetailsService {@Autowired@Lazyprivate PasswordEncoder passwordEncoder;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {//第一种方式:写死直接返回String password = passwordEncoder.encode("123456");System.out.println(password);return new User("liaozhiwei",password, AuthorityUtils.commaSeparatedStringToAuthorityList("admin"));//AuthorityUtils.commaSeparatedStringToAuthorityList("admin")用来为用户分配权限
/* // 第二种方式: 查数据库获取用户信息 rpc调用// 加载用户信息if (StringUtils.isEmpty(username)) {log.warn("用户登陆用户名为空:{}", username);throw new UsernameNotFoundException("用户名不能为空");}AppUserinfoEntity userUserinfo = getByUserName(username);if (null == userUserinfo) {log.warn("根据用户名没有查询到对应的用户信息:{}", username);}log.info("根据用户名:{}获取用户登陆信息:{}", username, userUserinfo);// 用户信息的封装 implements UserDetailsUserInfoDetails memberDetails = new UserInfoDetails(userUserinfo);return memberDetails;*/}@Autowiredprivate UserInfoFeignService userInfoFeignService;public AppUserinfoEntity getByUserName(String username) {// fegin获取用户信息ResultData<AppUserinfoEntity> resultData = userInfoFeignService.getUserByUserName(username);return resultData.getData();}
}
创建UserInfoDetails
代码如下(示例):
package com.example.springcloudsecurityoauth2demo.service;/*** @Author: liaozhiwei* @Description: TODO* @Date: Created in 19:04 2022/8/23*/
import com.example.springcloudsecurityoauth2demo.entity.AppUserinfoEntity;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Arrays;
import java.util.Collection;/*** @Author: liaozhiwei* @Description: TODO* @Date: Created in 19:13 2022/8/23*/
public class UserInfoDetails implements UserDetails {private AppUserinfoEntity userUserinfo;public UserInfoDetails(AppUserinfoEntity userUserinfo) {this.userUserinfo = userUserinfo;}@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {//返回当前用户的权限 BRAC user role authorityreturn Arrays.asList(new SimpleGrantedAuthority("TEST"));}// 获取用户密码(凭证)@Overridepublic String getPassword() {return userUserinfo.getCredential();}// 获取用户名@Overridepublic String getUsername() {return userUserinfo.getUserName();}// 判断帐号是否已经过期@Overridepublic boolean isAccountNonExpired() {return true;}// 判断帐号是否已被锁定@Overridepublic boolean isAccountNonLocked() {return true;}// 判断用户凭证是否已经过期@Overridepublic boolean isCredentialsNonExpired() {return true;}// 判断用户帐号是否已启用@Overridepublic boolean isEnabled() {return !userUserinfo.getUserStatus().equals("FREEZE");}public AppUserinfoEntity getUserUserinfo() {return userUserinfo;}}
创建JwtTokenUtil
代码如下(示例):
package com.example.springcloudsecurityoauth2demo.utils;import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/*** @Author: liaozhiwei* @Description: TODO* @Date: Created in 19:13 2022/8/24*/
public class JwtTokenUtil {// Token请求头public static final String TOKEN_HEADER = "Authorization";// Token前缀public static final String TOKEN_PREFIX = "Bearer ";// 签名主题public static final String SUBJECT = "app";// 过期时间public static final long EXPIRITION = 24 * 60 * 60 ;// 应用密钥public static final String APPSECRET_KEY = "123123";// 角色权限声明private static final String ROLE_CLAIMS = "role";/*** 生成Token*/public static String createToken(String username,String role) {Map<String,Object> map = new HashMap<>();map.put(ROLE_CLAIMS, role);String token = Jwts.builder().setSubject(username).setClaims(map).claim("username",username).setIssuedAt(new Date()).setExpiration(new Date(System.currentTimeMillis() + EXPIRITION)).signWith(SignatureAlgorithm.HS256, APPSECRET_KEY).compact();return token;}/*** 校验Token*/public static Claims checkJWT(String token) {try {final Claims claims = Jwts.parser().setSigningKey(APPSECRET_KEY).parseClaimsJws(token).getBody();return claims;} catch (Exception e) {e.printStackTrace();return null;}}/*** 从Token中获取username*/public static String getUsername(String token){Claims claims = Jwts.parser().setSigningKey(APPSECRET_KEY).parseClaimsJws(token).getBody();return claims.get("username").toString();}/*** 从Token中获取用户角色*/public static String getUserRole(String token){Claims claims = Jwts.parser().setSigningKey(APPSECRET_KEY).parseClaimsJws(token).getBody();return claims.get("role").toString();}/*** 校验Token是否过期*/public static boolean isExpiration(String token){Claims claims = Jwts.parser().setSigningKey(APPSECRET_KEY).parseClaimsJws(token).getBody();return claims.getExpiration().before(new Date());}
}
验证Spring Cloud Security Oauth2是否工作
首先要获取access_token,然后根据access_token认证授权
授权码模式
获取授权码
浏览器请求:http://127.0.0.1:8807/oauth/authorize?response_type=code&client_id=client
重定向为:http://127.0.0.1:8807/login
输入
用户名:liaozhiwei
密码:123456
如下图(示例):
授权登录之后会进行回调获取授权码
如下图(示例):
填入授权码code获取获取access_token
发起请求:http://127.0.0.1:8807/oauth/token?grant_type=authorization_code&client_id=client&client_secret=client_secret&code=12PVmF
如下图(示例):
password模式
http://127.0.0.1:8807/oauth/token?username=liaozhiwei&password=123456&grant_type=password&client_id=client&client_secret=client_secret&scope=all
如下图(示例):
刷新令牌获取access_token
http://127.0.0.1:8807/oauth/token?grant_type=refresh_token&client_id=client&client_secret=client_secret&refresh_token=
如下图(示例):
请求(示例):
http://127.0.0.1:8807/oauth/token?grant_type=refresh_token&client_id=client&client_secret=client_secret&refresh_token=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJsaWFvemhpd2VpIiwic2NvcGUiOlsiYWxsIl0sImF0aSI6ImZlNzJhYTE2LTM2M2EtNDFlNS05YWM2LTY0MDhkYjIzZWMyMCIsImFkZGl0aW9uYWxJbmZvIjp7InVzZXJOYW1lIjoibGlhb3poaXdlaSJ9LCJleHAiOjE2NjIyMTkzNjcsImF1dGhvcml0aWVzIjpbImFkbWluIl0sImp0aSI6IjFkNjRiMGI5LTkwNTQtNGMzYi05OWY1LTJjYzVjZGIxMTk2NSIsImNsaWVudF9pZCI6ImNsaWVudCJ9.cp6KF2XQJlNBRQTJXSvPpKh0t7jIAcJ9KegA60QIEK7dUYnMMf9b9I5Zu8AlmcQAq0tGGNTTLm1Wo42fYOWCElotRYDx-465kemhcuf-nHdGQ-qXDYMuhOKO5hlG1eajMD1ceoPK71dAxvEMRWAC5hk3_PmxlPIyaO-mDQJB3PhaDjEVNGhRncp7u5-UkPL3gZg1D9r-Yk1gDB9R1Qe7UDv0oBa_u07Vm1Q8i8tXnm6__6Xi2E16HBP0c872jc6voG2LCtcA5LMoPTHQlg3xAPodTuF7ncy188vXPAUzHf2Cp8ITH2lpJ68Jl0XQq8kz9vfv3r1_vK9_mbVAiys1UQ
发起请求使用access_token进行授权获取登录用户信息
http://127.0.0.1:8807/securityOauth2/getUserByTokenStore?access_token=
如下图(示例):
到这里其实已经可以验证我们通过授权码和密码模式获取access_token,然后通过access_token获取登录用户的消息了,当然还有更多的验证在代码里面已经实现了,这里我只对获取登录用户进行演示校验过程
创建spring-cloud-security-oauth2-sso-client-demo项目(实现单点登录)
项目代码:https://gitee.com/java_wxid/java_wxid/tree/master/demo/spring-cloud-security-oauth2-sso-client-demo
项目结构如下图(示例):
修改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><groupId>com.example</groupId><artifactId>spring-cloud-security-oauth2-sso-client-demo</artifactId><version>0.0.1-SNAPSHOT</version><name>spring-cloud-security-oauth2-sso-client-demo</name><description>Demo project for Spring Boot</description><!-- 属性配置--><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><java.version>1.8</java.version><!--引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件,进行依赖版本的管理,防止不兼容。在 https://dwz.cn/mcLIfNKt 文章中,Spring Cloud Alibaba 开发团队推荐了三者的依赖关系--><spring.boot.version>2.3.12.RELEASE</spring.boot.version><spring.cloud.version>Hoxton.SR12</spring.cloud.version><spring.cloud.alibaba.version>2.2.7.RELEASE</spring.cloud.alibaba.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!-- 代表web模块,在这个模块中含了许多JAR包,有spring相关的jar,内置tomcat服务器,jackson等,这些web项目中常用的的功能都会自动引入--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- Alibaba Nacos 配置 --><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId></dependency><!-- 在SpringBoot 2.4.x的版本之后,对于bootstrap.properties/bootstrap.yaml配置文件(我们合起来成为Bootstrap配置文件)的支持,其实这个jar包里什么都没有,就只有一个标识类Marker,用来标识要开启Bootstrap配置文件的支持,由于父类用了2.5.6版本需要导入如下的依赖--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-bootstrap</artifactId><version>3.1.0</version></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-oauth2</artifactId></dependency><!--JWT依赖--><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.1</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies><!--引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件,进行依赖版本的管理,防止不兼容。在 https://dwz.cn/mcLIfNKt 文章中,Spring Cloud Alibaba 开发团队推荐了三者的依赖关系--><dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>${spring.boot.version}</version><type>pom</type><scope>import</scope></dependency><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><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>
创建bootstrap.yml文件
代码如下(示例):
#bootstrap.yml优先级比application.yml优先级高
spring:#prefix−{spring.profile.active}.${file-extension}#nacos会根据当前环境去拼接配置名称查找相应配置文件,#示例:{spring.application.name}-{spring.profiles.active}-{spring.cloud.nacos.config.file-extension}#获取到值:nacos-autoconfig-service-dev.ymlprofiles:#开发环境dev,测试环境test,生产环境prodactive: devapplication:#配置应用的名称,用于获取配置name: security-oauth2-sso-clientmain:allow-bean-definition-overriding: truecloud:nacos:discovery:# 服务注册地址server-addr: ip:8848config:#nacos配置中心地址server-addr: ip:8848#配置中心的命名空间idnamespace: 9e50b6d9-6c3d-4e7a-b701-10f085e4b98d#配置分组,默认没有也可以group: DEFAULT_GROUP#配置文件后缀,用于拼接配置配置文件名称,目前只支持yaml和propertiesfile-extension: yaml#配置自动刷新refresh-enabled: true#配置文件的前缀,默认是application.name的值,如果配了prefix,就取prefix的值#prefix: nacos-autoconfig-service-${spring.profile.active}# 配置编码encode: UTF-8username: nacospassword: nacos
修改application.properties文件
代码如下(示例):
server.port=8808
server.servlet.session.cookie.name=OAUTH2-CLIENT-SESSIONID${server.port}
oauth2-server-url: http://127.0.0.1:8807
security.oauth2.client.client-id=client
security.oauth2.client.use-current-uri=false
security.oauth2.client.client-secret=client_secret
security.oauth2.client.user-authorization-uri=${oauth2-serverurl}/oauth/authorize
security.oauth2.client.access-token-uri=${oauth2-server-url}/oauth/token
security.oauth2.resource.jwt.key-uri=${oauth2-server-url}/oauth/token_key
修改SpringCloudSecurityOauth2SsoClientDemoApplication
代码如下(示例):
package com.luban.oauth2ssoclientdemo;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.security.oauth2.client.EnableOAuth2Sso;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;@SpringBootApplication
/*@EnableOAuth2Sso单点登录的原理简单来说就是:标注有@EnableOAuth2Sso的OAuth2 Client应用在通过某种
OAuth2授权流程获取访问令牌后(一般是授权码流程),通过访问令牌访问userDetails用户明细这个受保护资源服务,
获取用户信息后,将用户信息转换为Spring Security上下文中的认证后凭证Authentication,从而完成标注有
@EnableOAuth2Sso的OAuth2 Client应用自身的登录认证的过程。整个过程是基于OAuth2的SSO单点登录*/
@EnableOAuth2Sso
@EnableDiscoveryClient
public class SpringCloudSecurityOauth2SsoClientDemoApplication {public static void main(String[] args) {SpringApplication.run(SpringCloudSecurityOauth2SsoClientDemoApplication.class, args);}}
创建UserController
代码如下(示例):
package com.luban.oauth2ssoclientdemo.controller;import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;/*** @Author: liaozhiwei* @Description: TODO* @Date: Created in 09:24 2022/8/24*/
@RestController
@RequestMapping("/user")
public class UserController {@RequestMapping("/testCallback")public Object testCallback() {return "";}
}
校验请求是否有到授权服务器进行授权
浏览器发起请求:http://localhost:8808/user/getUserByAuthentication
重定向到登录页面:
输入
用户名:liaozhiwei
密码:123456
如下图(示例):
授权登录之会进行回调,返回授权码
如下图(示例):
拿到授权码获取access_token
http://127.0.0.1:8807/oauth/token?grant_type=authorization_code&client_id=client&client_secret=client_secret&code=fj7ivJ
如下图(示例):
到这里为止,可以发现单点登录也基本校验成功了
【java_wxid项目】【第七章】【Spring Cloud Security Oauth2集成】相关推荐
- gateway oauth2 对称加密_深入理解Spring Cloud Security OAuth2及JWT
因项目需要,需要和三方的oauth2服务器进行集成.网上关于spring cloud security oauth2的相关资料,一般都是讲如何配置,而能把这块原理讲透彻的比较少,这边自己做一下总结和整 ...
- 玩转Spring Cloud Security OAuth2身份认证扩展——电话号码+验证码认证
在程序的认证过程中,除了常规的用户名和密码方式(可以参考深入理解Spring Cloud Security OAuth2身份认证),也经常会出现电话号码+密码的方式:电话号码+验证码的方式:或者第三方 ...
- Spring Cloud Security OAuth2结合网关
文章目录 一.需求分析 二.服务搭建 2.1 注册中心 Eureka 搭建 2.2 网关 Zuul 搭建 2.2.1 转发明文token给微服务 2.2.2 微服务处理 token 2.2.3 Spr ...
- 【java_wxid项目】【第十六章】【Spring Cloud Alibaba Sentinel集成】
主项目链接:https://gitee.com/java_wxid/java_wxid 项目架构及博文总结: 点击:[使用Spring Boot快速构建应用] 点击:[使用Spring Cloud O ...
- 《深入理解 Spring Cloud 与微服务构建》第十六章 Spring Boot Security 详解
<深入理解 Spring Cloud 与微服务构建>第十六章 Spring Boot Security 详解 文章目录 <深入理解 Spring Cloud 与微服务构建>第十 ...
- 《深入理解 Spring Cloud 与微服务构建》第三章 Spring Cloud
<深入理解 Spring Cloud 与微服务构建>第三章 Spring Cloud 文章目录 <深入理解 Spring Cloud 与微服务构建>第三章 Spring Clo ...
- java B2B2C springmvc mybatis电子商务平台源码-Spring Cloud Security
一.SpringCloud Security简介 Spring Cloud Security提供了一组原语,用于构建安全的应用程序和服务,而且操作简便.可以在外部(或集中)进行大量 ...
- Spring Cloud Security:Oauth2实现单点登录
摘要 Spring Cloud Security 为构建安全的SpringBoot应用提供了一系列解决方案,结合Oauth2可以实现单点登录功能,本文将对其单点登录用法进行详细介绍. 单点登录简介 单 ...
- Spring Cloud Security:Oauth2结合JWT使用
摘要 Spring Cloud Security 为构建安全的SpringBoot应用提供了一系列解决方案,结合Oauth2还可以实现更多功能,比如使用JWT令牌存储信息,刷新令牌功能,本文将对其结合 ...
最新文章
- BCH升级日期将至,社区组织开始为11月“硬分叉”做准备
- Unity3D中暂停时的动画及粒子效果实现
- linux冒泡算法程序,用蛮力法解决冒泡排序 - linux-tao的个人空间 - OSCHINA - 中文开源技术交流社区...
- 安装node和pm2
- python判断路径是文件还是文件夹_python之判断是否是目录或文件
- luogu 3806
- Bootstrap学习笔记01【快速入门、栅格布局】
- opencv 手部识别_手势识别结合到VR头显中,有哪些难点?
- html-css练习题 (注册表单)
- python 怎么样才有output_[学]Python用户手册笔记_4_Input and Output
- 点击键盘上的“Next”button实现文本框焦点跳转
- ASP.NET MVC HandleErrorAttribute 和 远程链接
- 二级c语言上机题库下载,二级C语言上机题库(全).doc
- Android WideVine
- 今天过节,摔杯,逼宫,吃瓜吧?
- PostMan 快快走开, ApiFox 来了, ApiFox 强大的Api调用工具
- 免费合并多个PDF文件
- 【Django | 开发】面试招聘网站(增加csv,excel导出企业域账号集成日志管理功能)
- SQLAlchemy批量操作数据
- Web前端开发技术:实验14-16
热门文章
- java lucence使用案例_2、Lucene 最简单的使用(小例子)
- 虚拟机启动时出现“已启用侧通道缓解”的解决方法
- Altium Designer AD 2019最新各种元器件的原理图库、封装库、集成库、3D库,总量达四千多个,分类齐全。
- SNS网站Feed功能设计
- java字符串去重,精心整理
- Python队列|一文看懂python队列的几种类型
- 从零开始建立 EMQ X MQTT 服务器 的 K8S 集群
- “凛冬“已至二手车,天天拍车还能“守望“多久?
- 华为云服务之网络服务
- 科沃斯扫地机器人哪个型号最实用_扫地机器人哪个型号好_智能扫地机功能-科沃斯机器人官网-科沃斯机器人官网...