写在前面:各位看到此博客的小伙伴,如有不对的地方请及时通过私信我或者评论此博客的方式指出,以免误人子弟。多谢!如果我的博客对你有帮助,欢迎进行评论✏️✏️、点赞👍👍、收藏⭐️⭐️,满足一下我的虚荣心💖🙏🙏🙏 。
本系列将使用 Spring Cloud、 Spring Cloud Alibaba、SpringSecurity、OAuth2搭建一套包含认证服务、网关服务、服务提供者、服务调用者的简单微服务项目,目的是将设计到的相关技术综合应用。
目录
版本说明
首先说下我使用的Spring Cloud、 Spring Cloud Alibaba、SpringBoot等的版本,如下:
技术栈 | 版本 |
Spring Cloud | Hoxton.SR9 |
Spring Cloud Alibaba | 2.2.6.RELEASE |
Spring Boot | 2.3.2.RELEASE |
Mysql | 5.7 |
Nacos | 1.4.2 |
Sentinel | 1.8.1 |
版本的选择很重要,最好按照官网的版本对应关系选择,官网版本对应表如下:
版本说明 · alibaba/spring-cloud-alibaba Wiki · GitHub
第一篇先记录下认证授权的大体流程及需要引入的依赖。
依赖包
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
配置授权服务器
继承 AuthorizationServerConfigurerAdapter接口 并在实现类上加注解@EnableAuthorizationServer)标识这是一个授权服务器。
三个方法
可以看到只有这三个方法 :
public class AuthorizationServerConfigurerAdapter implements AuthorizationServerConfigurer {
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception{
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
}
}
每个方法的作用如下:
ClientDetailsServiceConfigurer:
配置客户端详情服务(ClientDetailsService),客户端详情信息在这里进行初始化,客户端信息可以存到内存中,也可以通过数据库来存储调取详情信息。
AuthorizationServerEndpointsConfigurer:
用来配置令牌(token)的访问端点和令牌服务(token services)。
AuthorizationServerSecurityConfigurer:
用来配置令牌端点(Token Endpoint)的安全约束。
上述三个方法就是授权服务的主要配置,可以关联记忆,既然要完成认证。
它首先得知道客户端信息从哪读取,因此要进行客户端详情配置;
既然要颁发token,那必须得定义token的相关endpoint及token如何存取,以及客户端支持哪些类型的token;
既然暴露出了一些endpoint,那对这些endpoint可以定义一些安全上的约束等。
配置客户端信息
ClientDetailsServiceConfigurer可以使用内存或JDBC来实现客户端详情服务(ClientDetailsService),ClientDetailsService负责查找ClientDetails,而ClientDetails有如下几个重要属性:
- clientId:标识客户的Id(必须的)。
- secret:客户端密码。
- scope:用来限制客户端的访问范围,默认为空,表示客户端拥有全部的访问范围。
- authorizedGrantTypes:此客户端可以使用的授权类型,默认为空。
- 其它属性参考下面客户端信息存储中的表结构字段说明。
客户端信息存储在数据库中时,需要建表,建表语句可以从如下网站找到:
spring-security-oauth/schema.sql at main · spring-attic/spring-security-oauth · GitHub
我用的是Mysql,对于上面链接里的建表语句,有某些字段为`LONGVARBINARY`类型,它对应mysql的`blob`类型,这需要改下,另外主键是varchar(255),如果又用的utf8mb4编码,主键长度会超限制,所以这个主键长度最好也要改一下。
每个表的作用如下:
oauth_client_details :客户端账号密码、授权、回调地址等重要信息;核心表。
oauth_access_token :存储access_token。
oauth_refresh_token :存储refresh_token。
oauth_client_token :存储从服务端获取的token数据。
oauth_code :存储授权码。
oauth_approvals :存储授权成功的客户端信息。
配置令牌访问端点
AuthorizationServerEndpointsConfigurer这个对象的实例可以完成令牌服务及令牌endpoint配置。
AuthorizationServerEndpointsConfigurer通过设定以下属性决定支持的授权类型:
- authenticationManager:认证管理器,密码模式需要。
- authorizationCodeServices:授权码模式需要。
- tokenGranter:如果设置了,那么授权将会交由你完全掌控,并忽略上面的几个属性,一般用作拓展用途,即标准的四种授权模式已经满足不了你的需求的时候,才会考虑使用这个。
配置授权端点
使用@EnableAuthorizationServer注解后,应用启动后将自动生成几个Endpoint:
/oauth/authorize:验证
/oauth/token:获取token
/oauth/confirm_access:用户授权
/oauth/error:认证失败
/oauth/check_token:资源服务器用来校验token
/oauth/token_key:如果使用JWT令牌,则公开用于令牌验证的公钥
说下/oauth/check_token和/oauth/token_key端点,他们都是用于检查令牌的,默认受保护denyAll()。使用tokenKeyAccess()和checkTokenAccess()方法会打开这些端点以供使用,如:
security.checkTokenAccess("permitAll()");
令牌端点的安全约束
AuthorizationServerSecurityConfigurer用来配置令牌端点的安全约束,如配置如下:
@Override
public void configure(AuthorizationServerSecurityConfigurer security){
security
.tokenKeyAccess("permitAll()") (1)
.checkTokenAccess("permitAll()") (2)
.allowFormAuthenticationForClients() (3);
}
含义如下:
( 1 )tokenkey这个 endpoint 当使用 JwtToken 且使用非对称加密时,资源服务用于获取公钥 而开放的,这里指这个 endpoint完全公开。( 2 ) checkToken 这个 endpoint 完全公开。( 3 ) 允许表单认证。
Token存储
OAuth2存储token值的方式由多种,所有的实现方式都是实现了TokenStore接口,主要有一下几种:
InMemoryTokenStore:token存储在本机的内存之中。
JdbcTokenStore:token存储在数据库之中。
JwtTokenStore:token不会存储到任何介质中。
RedisTokenStore:token存储在Redis数据库之中。
客户端信息存储
内存中。
数据库中。
客户端信息存储到 oauth_client_details 表中,建表语句可以从如下网站找到:
spring-security-oauth/schema.sql at main · spring-attic/spring-security-oauth · GitHub
每个表的作用如下:
oauth_client_details :客户端账号密码、授权、回调地址等重要信息;核心表。
oauth_access_token :存储access_token。
oauth_refresh_token :存储refresh_token。
oauth_client_token :存储从服务端获取的token数据。
oauth_code :存储授权码。
oauth_approvals :存储授权成功的客户端信息。
配置资源服务器
继承 ResourceServerConfigurerAdapter 接口并在实现类上添加注解 @EnableResourceServer标识这是一个资源服务器。
可以看到只有这两个方法 :
public class ResourceServerConfigurerAdapter implements ResourceServerConfigurer {
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
}
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated();
}
}
每个方法的作用如下:
configure(HttpSecurity http):可以配置哪些可以访问,哪些不可访问,默认情况下所有不在/oauth/**下的资源都是受保护的资源。
configure(ResourceServerSecurityConfigurer resources):先看下这个方法的解释
它可以为资源服务器添加一个特定的属性,如resourceId,也就是说我们一个授权服务器可能对应多个资源服务器,可以为每个资源服务器添加一个resourceId属性进行区分,可选,但建议使用,如果存在,auth服务器将进行验证。
配置WebSecurityConfig
引入security的依赖并做相关的配置,对资源进行保护。
通常需要复写一下几个方法:
public void configure(WebSecurity web) throws Exception {}
protected void configure(HttpSecurity http) throws Exception {}
public void configure(AuthenticationManagerBuilder auth) throws Exception {}
public AuthenticationManager authenticationManagerBean() throws Exception {}
每个方法的作用如下:
configure(WebSecurity web):可以配置忽略某些请求。
configure(HttpSecurity http):配置安全拦截机制控制资源的访问,可配置匹配哪些请求、哪些可以直接访问、哪些需要授权后访问。
authenticationManagerBean():配置验证管理的Bean。
configure(AuthenticationManagerBuilder auth):配置验证的用户信息源和密码加密策略,并向容器注入AuthenticationManager对象,这需要在OAuth2中配置(授权服务器),配置了AuthenticationManager密码验证才会生效。