Springboot 系列 (7) - Springboot+OAuth2(二) | 使用 Security 搭建基于 JDBC 验证的授权服务器
在 “ Springboot 系列 (6) - Springboot+OAuth2(一) | 使用 Security 搭建基于内存验证的授权服务器 ” 里的项目 SpringbootExample06 完成了一个基于内存验证的授权服务器。
本文将完全复制 SpringbootExample06 的代码和配置到新项目 SpringbootExample07,并在新项目 SpringbootExample07 的基础上修改代码和配置,搭建一个基于 JDBC 验证的授权服务器。
1. 配置数据库
1) XAMPP for Windows
https://www.apachefriends.org/download.html
本文安装版本 7.4.25,用到 XAMPP 的如下功能:
+ Apache 2.4.51
+ MariaDB 10.4.21 (MySQL的分支)
+ PHP 7.4.25 (VC15 X86 64bit thread safe) + PEAR
+ phpMyAdmin 5.1.1
创建数据库: springboot_example
2) 创建 table
(1) OAuth 相关 table,创建 src/main/resources/schema/oauth_schema.sql 文件
CREATE TABLE `clientdetails` ( `appId` varchar(128) NOT NULL, `resourceIds` varchar(256) DEFAULT NULL, `appSecret` varchar(256) DEFAULT NULL, `scope` varchar(256) DEFAULT NULL, `grantTypes` varchar(256) DEFAULT NULL, `redirectUrl` varchar(256) DEFAULT NULL, `authorities` varchar(256) DEFAULT NULL, `access_token_validity` int(11) DEFAULT NULL, `refresh_token_validity` int(11) DEFAULT NULL, `additionalInformation` varchar(4096) DEFAULT NULL, `autoApproveScopes` varchar(256) DEFAULT NULL, PRIMARY KEY (`appId`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE `oauth_access_token` ( `token_id` varchar(256) DEFAULT NULL, `token` blob, `authentication_id` varchar(128) NOT NULL, `user_name` varchar(256) DEFAULT NULL, `client_id` varchar(256) DEFAULT NULL, `authentication` blob, `refresh_token` varchar(256) DEFAULT NULL, PRIMARY KEY (`authentication_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE `oauth_approvals` ( `userId` varchar(256) DEFAULT NULL, `clientId` varchar(256) DEFAULT NULL, `scope` varchar(256) 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(256) DEFAULT NULL, `client_secret` varchar(256) DEFAULT NULL, `scope` varchar(256) DEFAULT NULL, `authorized_grant_types` varchar(256) DEFAULT NULL, `web_server_redirect_uri` varchar(256) DEFAULT NULL, `authorities` varchar(256) DEFAULT NULL, `access_token_validity` int(11) DEFAULT NULL, `refresh_token_validity` int(11) DEFAULT NULL, `additional_information` varchar(4096) DEFAULT NULL, `autoapprove` varchar(256) DEFAULT NULL, PRIMARY KEY (`client_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE `oauth_client_token` ( `token_id` varchar(256) DEFAULT NULL, `token` blob, `authentication_id` varchar(128) NOT NULL, `user_name` varchar(256) DEFAULT NULL, `client_id` varchar(256) DEFAULT NULL, PRIMARY KEY (`authentication_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE `oauth_code` ( `code` varchar(256) DEFAULT NULL, `authentication` blob ) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE `oauth_refresh_token` ( `token_id` varchar(256) DEFAULT NULL, `token` blob, `authentication` blob ) ENGINE=InnoDB DEFAULT CHARSET=utf8; INSERT INTO `oauth_client_details` VALUES ('1', 'auth_1', '4eti4hAaux', 'All', 'authorization_code,refresh_token', '/oauth/test/code/callback', NULL, NULL, NULL, NULL, 'true'); INSERT INTO `oauth_client_details` VALUES ('2', 'auth_2', 'xGJoD2i2lj', 'All', 'implicit', '/oauth/test/implicit/callback', NULL, NULL, NULL, NULL, 'true'); INSERT INTO `oauth_client_details` VALUES ('3', 'auth_3', '2lo2ijxJ3e', 'All', 'password,client_credentials', NULL, NULL, NULL, NULL, NULL, 'true');
(2) 用户验证相关 table,创建 src/main/resources/schema/users_schema.sql 文件
1 CREATE TABLE `users` ( 2 `username` varchar(50) NOT NULL, 3 `password` varchar(50) NOT NULL, 4 `enabled` tinyint(4) NOT NULL DEFAULT '1', 5 PRIMARY KEY (`username`) 6 ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 7 8 CREATE TABLE `user_authorities` ( 9 `authority_id` int(11) NOT NULL AUTO_INCREMENT, 10 `username` varchar(50) NOT NULL, 11 `authority` varchar(100) NOT NULL, 12 PRIMARY KEY (`authority_id`) 13 ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 14 15 INSERT INTO `users` VALUES ('admin', '123456', '1'),('user', '123456', '1'); 16 INSERT INTO `user_authorities` VALUES ('1', 'admin', 'ROLE_ADMIN'),('2', 'user', 'ROLE_USER');
在数据库 springboot_example 里,执行脚本 oauth_schema.sql 和 users_schema.sql。
2. 导入 MariaDB、 JDBC 依赖包
1) 修改 pom.xml
1 <project ... > 2 ... 3 <dependencies> 4 ... 5 6 <!-- MariaDB --> 7 <dependency> 8 <groupId>org.mariadb.jdbc</groupId> 9 <artifactId>mariadb-java-client</artifactId> 10 </dependency> 11 <!-- JDBC --> 12 <dependency> 13 <groupId>org.springframework.boot</groupId> 14 <artifactId>spring-boot-starter-data-jdbc</artifactId> 15 </dependency> 16 17 ... 18 </dependencies> 19 20 ... 21 </project>
在IDE中项目列表 -> SpringbootExample07 -> 点击鼠标右键 -> Maven -> Reload Project
2) 修改 src/main/resources/application.properties 文件,添加如下代码:
# 数据源连接信息
spring.datasource.driver-class-name=org.mariadb.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/springboot_example
spring.datasource.username=root
spring.datasource.password=123456
3. 配置 Security & OAuth2 (基于 JDBC 验证)
1) 修改 src/main/java/com/example/config/WebSecurityConfig.java 文件
package com.example.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.authentication.AuthenticationManager; // import javax.sql.DataSource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.crypto.password.NoOpPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.oauth2.provider.token.TokenStore; import org.springframework.security.oauth2.provider.token.store.JdbcTokenStore; @Configuration public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private DataSource dataSource; @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { // JDBC 模式 auth.jdbcAuthentication().dataSource(dataSource).passwordEncoder(NoOpPasswordEncoder.getInstance()) .usersByUsernameQuery("SELECT username,password,enabled FROM users WHERE username=?") .authoritiesByUsernameQuery("SELECT username,authority FROM user_authorities where username=?"); } @Override protected void configure(HttpSecurity http) throws Exception { // 配置认证 http.authorizeRequests() .antMatchers("/error", "/lib/**", "/oauth/**").permitAll() .anyRequest().authenticated() .and() .formLogin() .and() .csrf().disable(); // 关闭 csrf 保护功能,默认是开启的 } @Bean public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } @Bean public PasswordEncoder passwordEncoderBean() { // 密码明码保存 return NoOpPasswordEncoder.getInstance(); } @Bean public TokenStore tokenStoreBean() { // 使用 JDBC 保存 token return new JdbcTokenStore(dataSource); } }
注:这里定义的 AuthenticationManager、PasswordEncoder、TokenStore 是 Spring Bean,在 AuthorizationServerConfig 里会装配使用。
2) 修改 src/main/java/com/example/config/oauth2/AuthorizationServerConfig.java 文件
1 package com.example.config.oauth2; 2 3 import org.springframework.context.annotation.Configuration; 4 import org.springframework.beans.factory.annotation.Autowired; 5 import org.springframework.beans.factory.annotation.Qualifier; 6 import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer; 7 import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter; 8 import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer; 9 import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer; 10 import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer; 11 12 import javax.sql.DataSource; 13 import org.springframework.http.HttpMethod; 14 import org.springframework.security.crypto.password.PasswordEncoder; 15 import org.springframework.security.authentication.AuthenticationManager; 16 import org.springframework.security.oauth2.provider.token.TokenStore; 17 18 @Configuration 19 @EnableAuthorizationServer 20 public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { 21 @Autowired 22 @Qualifier("authenticationManagerBean") 23 private AuthenticationManager authenticationManager; 24 @Autowired 25 private DataSource dataSource; 26 @Autowired 27 @Qualifier("passwordEncoderBean") 28 private PasswordEncoder passwordEncoder; 29 @Autowired 30 @Qualifier("tokenStoreBean") 31 private TokenStore tokenStore; 32 33 @Override 34 public void configure(AuthorizationServerSecurityConfigurer authServer) { 35 // 访问权限控制 36 authServer.tokenKeyAccess("permitAll()") 37 .checkTokenAccess("isAuthenticated()")
38 .allowFormAuthenticationForClients(); 39 } 40 41 @Override 42 public void configure(ClientDetailsServiceConfigurer clients) throws Exception { 43 44 // JDBC 模式 45 clients.jdbc(dataSource); 46 47 } 48 49 @Override 50 public void configure(AuthorizationServerEndpointsConfigurer endpoints) { 51 52 endpoints.authenticationManager(authenticationManager) 53 .allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST) 54 .tokenStore(tokenStore); 55 56 } 57 58 }
注:.checkTokenAccess("isAuthenticated()") 表示是访问 /oauth/check_token 时,需要使用 client_id 和 client_secret 验证,如无需验证,可设置为 "permitAll()" 。
4. 测试实例 (Web 模式)
1) 授权码模式(Authorization Code)实例
略
2) 简化模式(Implicit)实例
略
3) 密码模式(Resource Owner Password Credentials)实例
略
4) 客户端模式(Client Credentials)实例
略
注:以上省略的实例代码,请参考 “Spring 系列 (6) - 在 Spring Boot 项目里使用 Security 和 OAuth2 搭建授权服务器(一)” 里的 “6. 测试实例 (Web 模式) ”。
--------------------------------------
示例代码:https://gitee.com/slksm/public-codes/tree/master/demos/springboot-series/SpringbootExample07
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全网最简单!3分钟用满血DeepSeek R1开发一款AI智能客服,零代码轻松接入微信、公众号、小程
· .NET 10 首个预览版发布,跨平台开发与性能全面提升
· 《HelloGitHub》第 107 期
· 全程使用 AI 从 0 到 1 写了个小工具
· 从文本到图像:SSE 如何助力 AI 内容实时呈现?(Typescript篇)