Spring Cloud OAuth2实现用户认证中心学习笔记
本文案例使用的IDE是Spring Tool Suite 4,当然你如果使用IntelliJIDEA也不会有什么差别
1、创建Spring Cloud OAuth2项目
1、打开Spring Tool Suite 4开发环境,点击File->New->Spring Starter Project,如下图:
项目的Name:SpringCloudOAuth2Server
Group:wongoing
Artifact:ms
Package:com.wongoing
这些信息都可以根据你的项目实际情况而更改。
2、点击“Next”按钮出现选择项目依赖对话框,如下图:
此处选择的SpringBoot版本是2.3.8
添加对Cloud OAuth2和Spring Web的依赖
3、点击“Finish”按钮完成项目创建,项目创建完毕后的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.3.8.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>wongoing</groupId>
<artifactId>ms</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>SpringCloudOAuth2Server</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.SR9</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</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>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
4、默认的项目启动类如下:
com.wongoing.SpringCloudOAuth2ServerApplication.java
package com.wongoing;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringCloudOAuth2ServerApplication {
public static void main(String[] args) {
SpringApplication.run(SpringCloudOAuth2ServerApplication.class, args);
}
}
2、在不使用数据库的情况下实现AuthenticationServer
1、在项目中创建一个包com.wongoing.oauth2.entity,用于存放相关的实体类。
2、在com.wongoing.oauth2.entity包下创建Role类和User类,其中Role实现org.springframework.security.core.GrantedAuthority接口和java.io.Serializable接口,User实现org.springframework.security.core.userdetails.UserDetails接口和java.io.Serializable接口
代码如下:
com.wongoing.oauth2.entity.Role.java
package com.wongoing.oauth2.entity;
import java.io.Serializable;
import org.springframework.security.core.GrantedAuthority;
public class Role implements GrantedAuthority, Serializable {
private Long id;
private String name;
public Role() { }
public Role(Long id, String name) {
this.id = id;
this.name = name;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return name;
}
@Override
public String getAuthority() {
// TODO Auto-generated method stub
return name;
}
}
com.wongoing.oauth2.entity.User.java
package com.wongoing.oauth2.entity;
import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
public class User implements UserDetails, Serializable {
private Long id;
private String username;
private String password;
private List<Role> authorities;
public User() { }
public User(Long id, String userName, String password, List<Role> authorities) {
this.id = id;
this.username = userName;
this.password = password;
this.authorities = authorities;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
public void setAuthorities(List<Role> authorities) {
this.authorities = authorities;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
// TODO Auto-generated method stub
return this.authorities;
}
@Override
public String getPassword() {
// TODO Auto-generated method stub
return this.password;
}
@Override
public String getUsername() {
// TODO Auto-generated method stub
return this.username;
}
@Override
public boolean isAccountNonExpired() {
// TODO Auto-generated method stub
return true;
}
@Override
public boolean isAccountNonLocked() {
// TODO Auto-generated method stub
return true;
}
@Override
public boolean isCredentialsNonExpired() {
// TODO Auto-generated method stub
return true;
}
@Override
public boolean isEnabled() {
// TODO Auto-generated method stub
return true;
}
}
3、在项目中创建一个包com.wongoing.oauth2.config,用于存放与AuthenticationServer相关的配置类。
3.1 在com.wongoing.oauth2.config包下创建DefaultPaswordConfig.java,代码如下:
com.wongoing.oauth2.config.DefaultPassowrdConfig.java
package com.wongoing.oauth2.config;
import java.io.Serializable;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Component;
/**
* 功能说明:定义默认密码工具类
* 修改说明:
* @author zheng
* @date 2021-1-19 15:54:22
* @version 0.1
*/
@Component
public class DefaultPasswordConfig extends BCryptPasswordEncoder implements Serializable {
}
3.2 在com.wongoing.oauth2.config包下创建DefaultTokenStoreConfig.java,代码如下:
com.wongoing.oauth2.config.DefaultTokenStoreConfig.java
package com.wongoing.oauth2.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore;
/**
* 功能说明:TokenStore工具类
* 修改说明:
* @author zheng
* @date 2021-1-19 16:48:46
* @version 0.1
*/
@Configuration
public class DefaultTokenStoreConfig {
@Bean
public TokenStore tokenStore() {
//将Token存储在内存中
return new InMemoryTokenStore();
}
}
3.3 在com.wongoing.oauth2.config包下创建UserDetailsServiceBean.java,代码如下:
com.wongoing.oauth2.config.UserDetailsServiceBean.java
package com.wongoing.oauth2.config;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
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.Component;
import com.wongoing.oauth2.entity.Role;
import com.wongoing.oauth2.entity.User;
/**
* 功能说明:用户服务类
* 修改说明:
* @author zheng
* @date 2021-1-19 14:53:26
* @version 0.1
*/
@Component
public class UserDetailsServiceBean implements UserDetailsService, Serializable {
@Autowired
private PasswordEncoder passwordEncoder;
//保存用户测试数据
private Map<String, User> users = null;
/**
* 功能说明:通过@PostConstruct定义Bean初始化方法
* 修改说明:
* @author zheng
* @date 2021-1-19 16:39:55
*/
@PostConstruct
public void init() {
//生成测试数据
this.users = new HashMap<String, User>() {{
put("admin", new User(1L, "admin", passwordEncoder.encode("456"), new ArrayList() {{ add(new Role(1L, "ADMIN")); }}));
put("zheng", new User(2L, "zheng", passwordEncoder.encode("789"), new ArrayList() {{ add(new Role(2L, "USER")); }}));
}};
}
/**
* 根据用户名获取用户详情
*/
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//后期可以通过注入UserDao,使用UserDao从数据库中查找用户
if (this.users.containsKey(username)) {
return this.users.get(username);
}
return null;
}
}
3.4 在com.wongoing.oauth2.config包下创建WebSecurityConfig类并继承org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter,代码如下:
com.wongoing.oauth2.config.WebSecurityConfig.java
package com.wongoing.oauth2.config;
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.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
/**
* 功能说明:Web安全配置类(Spring Cloud OAuth2中强制要求使用此类)
* 修改说明:
* @author zheng
* @date 2021-1-19 15:58:43
* @version 0.1
*/
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private PasswordEncoder passwordEncoder;
@Autowired
private UserDetailsService userDetailsService;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.and()
.csrf().disable()
.httpBasic();
}
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/favor.ioc"); //配置/favor.ioc不需要进行安全拦截
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(this.passwordEncoder);
}
/**
* 这一步的配置是必不可少的,否则SpringBoot会自动配置一个AuthenticationManager,覆盖掉内存中的用户
* @return 认证管理对象
*/
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
3.5 在com.wongoing.oauth2.config包下创建AuthorizationServerConfig类并继承org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;,代码如下:
com.wongoing.oauth2.config.AuthorizationServerConfig
package com.wongoing.oauth2.config;
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.core.userdetails.UserDetailsService;
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.TokenStore;
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
@Autowired
private PasswordEncoder passwordEncoder;
@Autowired
private TokenStore tokenStore;
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private UserDetailsService userDetailsService;
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
// 将客户端的信息存储在内存中
// 此处客户端信息先写死,后面会写一个从数据库中读取客户端信息的例子
clients.inMemory()
.withClient("client1")
.secret(this.passwordEncoder.encode("123456"))
.authorizedGrantTypes("authorization_code", "client_credentials", "refresh_token", "password") //此处设置支持4种授权类型
.redirectUris("http://www.baidu.com")
.scopes("all");
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
// 配置Token的存储方式
endpoints.tokenStore(this.tokenStore)
// 注入WebSecurityConfig配置的bean
.authenticationManager(authenticationManager)
// 读取用户的验证信息
.userDetailsService(userDetailsService)
//配置允许通过GET请求获取access_token
.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST);
}
@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
// 对获取Token的请求不再拦截
oauthServer
.tokenKeyAccess("permitAll()")
// 验证获取Token的验证信息
.checkTokenAccess("isAuthenticated()")
//这个如果配置支持allowFormAuthenticationForClients的,且对/oauth/token请求的参数中有client_id和client-secret的会走ClientCredentialsTokenEndpointFilter来保护
//如果没有支持allowFormAuthenticationForClients或者有支持但对/oauth/token请求的参数中没有client_id和client_secret的,走basic认证保护
.allowFormAuthenticationForClients();
}
}
3、测试AuthenticationServer
我们通过Spring Cloud OAuth2的2个端点来进行验证测试
端点1:/oauth/authorize,此端点用于获取授权码code
端点2:/oauth/token,此端点用于获取access_token
3.1 授权码方式(grant_type=authorization_code)验证测试
1、首先通过项目入口程序启动此Spring Boot项目。
2、通过oauth/authorize端点获取授权码,打开浏览器在地址栏输入:
http://localhost:8080/oauth/authorize?response_type=code&client_id=client1&client_secret=123456&redirect_uri=http://www.baidu.com
如下图:
第一次进入登录页面,如下:
3、输入用户名admin,密码456,然后点击Sign in,如下以下界面
4、选择Approve,然后点击Authorize,会进入redirect_uri,如下图:
从上图中可以得到code的值
5、使用授权码获取access_token,打开postman,输入地址如下:
http://localhost:8080/oauth/token?grant_type=authorization_code&code=ISEM95&redirect_uri=http://www.baidu.com&client_id=client1&client_secret=123456
如下图:
3.2 客户端方式(grant_type=client_credentials)验证测试
1、在postman中输入以下地址进行访问:
http://localhost:8080/oauth/token?grant_type=client_credentials&client_id=client1&client_secret=123456
如下图:
3.3 用户名密码方式(grant_type=password)验证测试
1、在postman中输入以下地址进行访问:
- http://localhost:8080/oauth/token?grant_type=password&client_id=client1&client_secret=123456&username=admin&password=456
- http://localhost:8080/oauth/token?grant_type=password&client_id=client1&client_secret=123456&username=zheng&password=789
如下图:
3.4 刷新token(grant_type=refresh_token)测试
1、在postman中输入以下地址进行访问
http://localhost:8080/oauth/token?grant_type=refresh_token&client_id=client1&client_secret=123456&refresh_token=31b124a6-d4cf-48e3-9800-39f80d4443b2
如下图:
4、不使用数据库版本的AuthorizationServer源码下载
不使用数据库版本的AuthorizationServer源码下载
5、基于数据库的情况下实现AuthenticationServer
5.1 创建oauth2相关表
可以参考官方建议的表结构,地址如下:
https://github.com/spring-projects/spring-security-oauth/blob/master/spring-security-oauth2/src/test/resources/schema.sql
以下是整理的mysql版本的建表语句
CREATE TABLE oauth_client_details (
client_id VARCHAR(256) PRIMARY KEY,
resource_ids VARCHAR(256),
client_secret VARCHAR(256),
scope VARCHAR(256),
authorized_grant_types VARCHAR(256),
web_server_redirect_uri VARCHAR(256),
authorities VARCHAR(256),
access_token_validity INTEGER,
refresh_token_validity INTEGER,
additional_information VARCHAR(4096),
autoapprove VARCHAR(256)
);
CREATE TABLE oauth_client_token (
token_id VARCHAR(256),
token BLOB,
authentication_id VARCHAR(25) PRIMARY KEY,
user_name VARCHAR(256),
client_id VARCHAR(256)
);
CREATE TABLE oauth_access_token (
token_id VARCHAR(256),
token BLOB,
authentication_id VARCHAR(250) PRIMARY KEY,
user_name VARCHAR(256),
client_id VARCHAR(256),
authentication BLOB,
refresh_token VARCHAR(256)
);
CREATE TABLE oauth_refresh_token (
token_id VARCHAR(256),
token BLOB,
authentication BLOB
);
CREATE TABLE oauth_code (
CODE VARCHAR(256), authentication BLOB
);
CREATE TABLE oauth_approvals (
userId VARCHAR(256),
clientId VARCHAR(256),
scope VARCHAR(256),
STATUS VARCHAR(10),
expiresAt TIMESTAMP,
lastModifiedAt TIMESTAMP
);
-- customized oauth_client_details table
CREATE TABLE ClientDetails (
appId VARCHAR(256) PRIMARY KEY,
resourceIds VARCHAR(256),
appSecret VARCHAR(256),
scope VARCHAR(256),
grantTypes VARCHAR(256),
redirectUrl VARCHAR(256),
authorities VARCHAR(256),
access_token_validity INTEGER,
refresh_token_validity INTEGER,
additionalInformation VARCHAR(4096),
autoApproveScopes VARCHAR(256)
);
创建一个mysql的数据库,然后在数据中执行上面的建表语句。
5.2 在数据表中增加client的定义
INSERT INTO oauth_client_details(client_id,client_secret,scope,authorized_grant_types,web_server_redirect_uri) VALUES('webApp','$2a$10$CgTKlYAXYJeWX9Zem3hCSutmSjA2DrwDiPW5ta/qRO2Gv3Pk/n2yC','all','authorization_code,password,refresh_token,client_credentials','http://www.baidu.com');
INSERT INTO oauth_client_details(client_id,client_secret,scope,authorized_grant_types) VALUES('app','$2a$10$6vUDS.ai.Szi.S6vU/8nluoa.JoOJagy9G4ak4yBxjdbJlDJQTD1.','all','password,refresh_token');
上面的第1条记录的client_secret值是123,第2条记录的client_secret的值是456。
5.3 修改pom.xml增加以下依赖,这里使用了阿里的druid数据库连接池
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.4</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
这样完整的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.3.8.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>wongoing</groupId>
<artifactId>ms</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>SpringCloudOAuth2Server</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.SR9</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.4</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</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>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
5.4 把application.properties修改为application.yml,增加数据源的相关配置
application.yml内容如下:
spring:
datasource:
name: dataSource
url: jdbc:mysql://localhost:3306/wcm_uaa?useUnicode=true&characterEncoding=utf-8&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC
username: uid
password: ******
driver-class-name: com.mysql.cj.jdbc.Driver
###################以下为druid增加的配置###########################
type: com.alibaba.druid.pool.DruidDataSource
druid:
db-type: mysql
# 下面为连接池的补充设置,应用到上面所有数据源中
# 初始化大小,最小,最大
initial-size: 5
# 最小连接池数量
min-idle: 5
# 最大连接池数量
max-active: 20
# 配置获取连接等待超时的时间
max-wait: 60000
#申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。
test-while-idle: true
#既作为检测的间隔时间又作为testWhileIdel执行的依据
time-between-eviction-runs-millis: 60000
#销毁线程时检测当前连接的最后活动时间和当前时间差大于该值时,关闭当前连接
min-evictable-idle-time-millis: 30000
#用来检测连接是否有效的sql 必须是一个查询语句
#mssql中为 select 1
#mysql中为 select 'x'
#oracle中为 select 1 from dual
validation-query: select 1
#申请连接时会执行validationQuery检测连接是否有效,开启会降低性能,默认为true
test-on-borrow: false
#归还连接时会执行validationQuery检测连接是否有效,开启会降低性能,默认为true
test-on-return: false
#当数据库抛出不可恢复的异常时,抛弃该连接
#exception-sorter: true
#是否缓存preparedStatement,mysql5.5+建议开启
pool-prepared-statements: true
#当值大于0时poolPreparedStatements会自动修改为true
max-pool-prepared-statement-per-connection-size: 20
#配置扩展插件
#配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
filters: stat,wall,slf4j
#通过connectProperties属性来打开mergeSql功能;慢SQL记录
connection-properties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
#合并多个DruidDataSource的监控数据
use-global-data-source-stat: true
#设置访问druid监控页的账号和密码,默认没有
stat-view-servlet:
login-username: admin
login-password: admin
enabled: true
#开启web filter
web-stat-filter:
enabled: true
###############以上为配置druid添加的配置#######################################
5.5 修改DefaultTokenStoreConfig为数据库存储Token
修改后的com.wongoing.oauth2.config.DefaultTokenStoreConfig.java代码如下:
package com.wongoing.oauth2.config;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JdbcTokenStore;
/**
* 功能说明:TokenStore工具类
* 修改说明:
* @author zheng
* @date 2021-1-19 16:48:46
* @version 0.1
*/
@Configuration
public class DefaultTokenStoreConfig {
@Autowired
private DataSource dataSource;
@Bean
public TokenStore tokenStore() {
//将Token存储在内存中
// return new InMemoryTokenStore();
//将Token存储在数据库中
return new JdbcTokenStore(this.dataSource);
}
}
5.7 删除原来的AuthorizationServerConfig类
删除com.wongoing.oauth2.config.AuthorizationServerConfig.java或者注释掉类前面的2个注解,注释掉注解后的代码如下
package com.wongoing.oauth2.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
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.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.TokenStore;
//@Configuration
//@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
@Autowired
private PasswordEncoder passwordEncoder;
@Autowired
private TokenStore tokenStore;
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private UserDetailsService userDetailsService;
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
// 将客户端的信息存储在内存中
// 此处客户端信息先写死,后面会写一个从数据库中读取客户端信息的例子
clients.inMemory()
.withClient("client1")
.secret(this.passwordEncoder.encode("123456"))
.redirectUris("http://www.baidu.com")
.authorizedGrantTypes("authorization_code", "client_credentials", "refresh_token", "password") //此处设置支持4种授权类型
.scopes("all");
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
// 配置Token的存储方式
endpoints.tokenStore(this.tokenStore)
// 注入WebSecurityConfig配置的bean
.authenticationManager(authenticationManager)
// 读取用户的验证信息
.userDetailsService(userDetailsService)
//配置允许通过GET请求获取access_token
.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST);
}
@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
// 对获取Token的请求不再拦截
oauthServer
.tokenKeyAccess("permitAll()")
// 验证获取Token的验证信息
.checkTokenAccess("isAuthenticated()")
//这个如果配置支持allowFormAuthenticationForClients的,且对/oauth/token请求的参数中有client_id和client-secret的会走ClientCredentialsTokenEndpointFilter来保护
//如果没有支持allowFormAuthenticationForClients或者有支持但对/oauth/token请求的参数中没有client_id和client_secret的,走basic认证保护
.allowFormAuthenticationForClients();
}
}
5.8 增加AuthorizationServerConfigByDataSource.java类
在com.wongoing.oauth2.config包下创建AuthorizationServerConfigByDataSource类并继承AuthorizationServerConfigurerAdapter,代码如下:
com.wongoing.oauth2.config.AuthorizationServerConfigByDataSource.java
package com.wongoing.oauth2.config;
import javax.sql.DataSource;
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.authentication.AuthenticationProvider;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.core.userdetails.UserDetailsService;
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.TokenStore;
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfigByDataSource extends AuthorizationServerConfigurerAdapter {
@Autowired
private DataSource dataSource;
@Autowired
private PasswordEncoder passwordEncoder;
@Autowired
private TokenStore tokenStore;
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private UserDetailsService userDetailsService;
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.jdbc(this.dataSource); //会从数据库的oauth_client_details表中获取client的定义
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenStore(this.tokenStore)
.authenticationManager(this.authenticationManager)
.userDetailsService(this.userDetailsService)
.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST); //允许使用GET请求访问/oauth/token端点
}
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security.tokenKeyAccess("permitAll()")
.checkTokenAccess("isAuthenticated")
.allowFormAuthenticationForClients();
}
@Bean
public AuthenticationProvider daoAuthenticationProvider() {
DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
daoAuthenticationProvider.setUserDetailsService(userDetailsService);
daoAuthenticationProvider.setHideUserNotFoundExceptions(false);
daoAuthenticationProvider.setPasswordEncoder(passwordEncoder);
return daoAuthenticationProvider;
}
}
6、测试基于数据库实现的AuthenticationServer
基于数据库实现的验证服务器在测试时传递的client_id和client_secret参数一定要与oauth_client_details表中的数据对应上
6.1 授权码方式(grant_type=authorization_code)验证测试
1、首先通过项目入口程序启动此Spring Boot项目。
2、通过oauth/authorize端点获取授权码,打开浏览器在地址栏输入:
http://localhost:8080/oauth/authorize?response_type=code&client_id=webApp&client_secret=123&redirect_uri=http://www.baidu.com
后续步骤参照3.1的步骤进行测试。
6.2 客户端方式(grant_type=client_credentials)验证测试
1、在postman中输入以下地址进行访问:
http://localhost:8080/oauth/token?grant_type=client_credentials&client_id=webApp&client_secret=123
如下图:
同时在数据库中查询oauth_access_token,可以看到token已经存到数据表中了,如下图:
6.3 用户名密码方式(grant_type=password)验证测试
1、在postman中输入以下地址进行访问:
- http://localhost:8080/oauth/token?grant_type=password&client_id=webApp&client_secret=123&username=admin&password=456
- http://localhost:8080/oauth/token?grant_type=password&client_id=app&client_secret=456&username=zheng&password=789
如下图:
6.4 刷新token(grant_type=refresh_token)测试
1、在postman中输入以下地址进行访问
http://localhost:8080/oauth/token?grant_type=refresh_token&client_id=webApp&client_secret=123&refresh_token=af9bb496-290d-431e-a2f4-f4000f3bc390
如下图: