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

 

posted @   垄山小站  阅读(635)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全网最简单!3分钟用满血DeepSeek R1开发一款AI智能客服,零代码轻松接入微信、公众号、小程
· .NET 10 首个预览版发布,跨平台开发与性能全面提升
· 《HelloGitHub》第 107 期
· 全程使用 AI 从 0 到 1 写了个小工具
· 从文本到图像:SSE 如何助力 AI 内容实时呈现?(Typescript篇)
点击右上角即可分享
微信分享提示