Spring Cloud Oauth2 快速入门

一、基于内存+token方式

1.认证中心配置

引入依赖

	<!-- eurek依赖 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-oauth2</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.security.oauth.boot</groupId>
                    <artifactId>spring-security-oauth2-autoconfigure</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>org.springframework.security.oauth.boot</groupId>
            <artifactId>spring-security-oauth2-autoconfigure</artifactId>
            <version>2.1.11.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.security.oauth</groupId>
            <artifactId>spring-security-oauth2</artifactId>
            <version>2.3.4.RELEASE</version>
        </dependency>

编辑yml配置文件

server:
  port: 9999
spring:
  application:
    name: lagou-cloud-oauth-server
eureka:
  client:
    service-url:
      defaultZone: http://LagouCloudEurekaServerA:8761/eureka/,http://LagouCloudEurekaServerB:8762/eureka/
    registry-fetch-interval-seconds: 30 #多久拉取一次服务列表 默认30
  instance:
    prefer-ip-address: true
    instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}:@project.version@

主启动类

@EnableAuthorizationServer //开启认证服务器功能
@EnableDiscoveryClient
@SpringBootApplication
public class Oauth2ServerApplication9999 {
    public static void main(String[] args) {
        SpringApplication.run(Oauth2ServerApplication9999.class, args);
    }
}

创建配置类(OauthServerConfig)


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.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore;

/**
 * Oauth2 Server配置类需要继承特定父类 AuthorizationServerConfigurerAdapter
 *
 * @remark
 * @Author pengheng
 * @date 2021/5/25 11:10
 */
@Configuration
public class OauthServerConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private AuthenticationManager authenticationManager;

    /**
     * 认证服务器通过api接口方式对外提供服务
     *
     * @param security
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        super.configure(security);
        security
                //允许客户端表单认证
                .allowFormAuthenticationForClients()
                //开启/oauth/token_key的访问权限 默认=denyAll()
                .tokenKeyAccess("permitAll()")
                //开启端口/oauth/check_token的访问权限 默认=denyAll()
                .checkTokenAccess("permitAll()");
    }

    /**
     * 客户端详情配置
     * 办法client_id等必要参数,表明客户端身份
     *
     * @param clients
     * @throws Exception
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        super.configure(clients);
        //客户端信息存储位置 可以在内存中,也可以在数据库中
        clients.inMemory()
                //添加客户端配置,指定client_id
                .withClient("client_lagou")
                //指定客户端所能访问的密码
                .secret("abcxyz")
                //指定客户端所能访问的资源
                .resourceIds("autodeliver")
                //认证类型/令牌颁发模式
                .authorizedGrantTypes("password", "refresh_token")
                //客户端权限范围
                .scopes("all");
    }

    /**
     * 配置token令牌相关信息(token存储在这里配置)
     *
     * @param endpoints
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        super.configure(endpoints);
        endpoints
                //指定token的存储方式
                .tokenStore(tokenStore())
                //token服务的描述,比如有效时间等
                .tokenServices(authorizationServerTokenServices())
                //指定认证管理器
                .authenticationManager(authenticationManager)
                //允许的http请求方式
                .allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST);
    }

    /**
     * 创建令牌存储对象(内存、数据库、jwt、redis)
     *
     * @return
     */
    public TokenStore tokenStore() {
        return new InMemoryTokenStore();
    }

    public AuthorizationServerTokenServices authorizationServerTokenServices() {
        DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
        //是否支持令牌刷新
        defaultTokenServices.setSupportRefreshToken(true);
        //设置令牌生成方案
        defaultTokenServices.setTokenStore(tokenStore());
        //设置令牌有效时间(⼀般设置为2个⼩时)
        defaultTokenServices.setAccessTokenValiditySeconds(20);
        //令牌刷新时间
        defaultTokenServices.setRefreshTokenValiditySeconds(60 * 60 * 24 * 3);
        return defaultTokenServices;
    }
}

创建配置类(SecurityConfig)


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.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

import java.util.ArrayList;

/**
 * @remark
 * @Author pengheng
 * @date 2021/5/25 13:39
 */
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    /**
     * 注册⼀个认证管理器对象到容器
     */
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    /**
     * 密码编码对象(密码不进⾏加密处理)
     *
     * @return
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return NoOpPasswordEncoder.getInstance();
    }

    @Autowired
    private PasswordEncoder passwordEncoder;

    /**
     * 处理⽤户名和密码验证事宜
     * 1)客户端传递username和password参数到认证服务器
     * 2)⼀般来说,username和password会存储在数据库中的⽤户表中
     * 3)根据⽤户表中数据,验证当前传递过来的⽤户信息的合法性
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth)
            throws Exception {
        // 在这个⽅法中就可以去关联数据库了,当前我们先把⽤户信息配置在内存中
        // 实例化⼀个⽤户对象(相当于数据表中的⼀条⽤户记录)
        UserDetails user = new User("admin", "123456", new ArrayList<>());
        auth.inMemoryAuthentication().withUser(user).passwordEncoder(passwordEncoder);
    }
}

校验服务器测试

  • 生成token
    http://10.0.0.45:9999/oauth/token?client_secret=abcxyz&grant_type=password&username=admin&password=123456&client_id=client_lagou
{
    "access_token": "a7fb8e4d-304e-4dfb-b518-729a1a24f3f3",
    "token_type": "bearer",
    "refresh_token": "a3ad7612-29a0-49c8-81bd-ec1f0e30c50d",
    "expires_in": 17,
    "scope": "all"
}
  • 校验token

http://localhost:9999/oauth/check_token?token=a7fb8e4d-304e-4dfb-b518-729a1a24f3f3

{
    "aud": [
        "autodeliver"
    ],
    "active": true,
    "exp": 1621927869,
    "user_name": "admin",
    "client_id": "client_lagou",
    "scope": [
        "all"
    ]
}
  • 刷新token

http://localhost:9999/oauth/token?grant_type=refresh_token&client_id=client_lagou&client_secret=abcxyz&refresh_token=a3ad7612-29a0-49c8-81bd-ec1f0e30c50d

{
    "access_token": "a7fb8e4d-304e-4dfb-b518-729a1a24f3f3",
    "token_type": "bearer",
    "refresh_token": "a3ad7612-29a0-49c8-81bd-ec1f0e30c50d",
    "expires_in": 20,
    "scope": "all"
}

2.资源服务器配置

引入依赖(与认证中心相同)

	<!-- eureka 客户端依赖 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-oauth2</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.security.oauth.boot</groupId>
                    <artifactId>spring-security-oauth2-autoconfigure</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>org.springframework.security.oauth.boot</groupId>
            <artifactId>spring-security-oauth2-autoconfigure</artifactId>
            <version>2.1.11.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.security.oauth</groupId>
            <artifactId>spring-security-oauth2</artifactId>
            <version>2.3.4.RELEASE</version>
        </dependency>

创建配置类(ResourceServerConfig)

@Configuration
@EnableResourceServer //开启资源服务器功能
@EnableWebSecurity //开启web访问安全
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {

    /**
     * 该方法用于定义志愿服务期向远程服务器发起请求,进行token校验等事宜
     *
     * @param resources
     * @throws Exception
     */
    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
        resources.resourceId("autodeliver");
        RemoteTokenServices remoteTokenServices = new RemoteTokenServices();
        //设置校验token的url
        remoteTokenServices.setCheckTokenEndpointUrl("http://localhost:9999/oauth/check_token");
        //设置客户端id和客户端密钥
        remoteTokenServices.setClientId("client_lagou");
        remoteTokenServices.setClientSecret("abcxyz");
        resources.tokenServices(remoteTokenServices);
    }

    /**
     * 配置API接口认证,设置指定接口过滤或放行
     *
     * @param http
     * @throws Exception
     */
    @Override
    public void configure(HttpSecurity http) throws Exception {
        http
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
                .and()
                .authorizeRequests()
                //指定/autodeliver开头的都要验证
                .antMatchers("/autodeliver/**").authenticated()
                .antMatchers("/demo/**").authenticated()
                .anyRequest().permitAll();

    }
}

测试

请求被拦截

http://localhost:8096/autodeliver/checkState/1545132

响应:

{
    "error": "unauthorized",
    "error_description": "Full authentication is required to access this resource"
}

正常请求

http://localhost:8096/demo/test?access_token=2b9c1027-1525-40e3-8163-d6a8699b1a0a

响应:

demo-test

二、基于内存+Jwt 令牌方式

上述demo中,认证服务器颁发令牌都存在认证服务器内,后期访问资源服务器都要携带token去认证服务器去校验token有效性,如果资源服务器过多,认证服务器压力会很大。我们可以通过jwt机制进行改造,由于jwt是一个特殊的携带信息的加密字符,使用jwt机制之后资源服务器不需要访问认证服务器。JWT介绍

1.认证中心改造(修改OauthServerConfig配置)

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.jwt.crypto.sign.MacSigner;
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.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;

/**
 * Oauth2 Server配置类需要继承特定父类 AuthorizationServerConfigurerAdapter
 *
 * @remark
 * @Author pengheng
 * @date 2021/5/25 11:10
 */
@Configuration
public class OauthServerConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private AuthenticationManager authenticationManager;

    /**
     * 认证服务器通过api接口方式对外提供服务
     *
     * @param security
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        super.configure(security);
        security
                //允许客户端表单认证
                .allowFormAuthenticationForClients()
                //开启/oauth/token_key的访问权限 默认=denyAll()
                .tokenKeyAccess("permitAll()")
                //开启端口/oauth/check_token的访问权限 默认=denyAll()
                .checkTokenAccess("permitAll()");
    }

    /**
     * 客户端详情配置
     * 办法client_id等必要参数,表明客户端身份
     *
     * @param clients
     * @throws Exception
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        super.configure(clients);
        //客户端信息存储位置 可以在内存中,也可以在数据库中
        clients.inMemory()
                //添加客户端配置,指定client_id
                .withClient("client_lagou")
                //指定客户端所能访问的密码
                .secret("abcxyz")
                //指定客户端所能访问的资源
                .resourceIds("autodeliver")
                //认证类型/令牌颁发模式
                .authorizedGrantTypes("password", "refresh_token")
                //客户端权限范围
                .scopes("all");
    }

    /**
     * 配置token令牌相关信息(token存储在这里配置)
     *
     * @param endpoints
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        super.configure(endpoints);
        endpoints
                //指定token的存储方式
                .tokenStore(tokenStore())
                //token服务的描述,比如有效时间等
                .tokenServices(authorizationServerTokenServices())
                //指定认证管理器
                .authenticationManager(authenticationManager)
                //允许的http请求方式
                .allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST);
    }

    /**
     * 创建令牌存储对象(内存、数据库、jwt、redis)
     *
     * @return
     */
    public TokenStore tokenStore() {
//        return new InMemoryTokenStore();
        return new JwtTokenStore(jwtAccessTokenConverter());
    }

    private String sign_key = "pengheng";
    /**
     * 返回jwt令牌转换器
     * @return
     */
    public JwtAccessTokenConverter jwtAccessTokenConverter(){
        JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
        jwtAccessTokenConverter.setSigningKey(sign_key);
        jwtAccessTokenConverter.setVerifier(new MacSigner(sign_key));

        return jwtAccessTokenConverter;
    }

    public AuthorizationServerTokenServices authorizationServerTokenServices() {
        DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
        //是否支持令牌刷新
        defaultTokenServices.setSupportRefreshToken(true);
        //设置令牌生成方案
        defaultTokenServices.setTokenStore(tokenStore());
        //针对jwt令牌添加
        defaultTokenServices.setTokenEnhancer(jwtAccessTokenConverter());
        //设置令牌有效时间(⼀般设置为2个⼩时)
        defaultTokenServices.setAccessTokenValiditySeconds(20);
        //令牌刷新时间
        defaultTokenServices.setRefreshTokenValiditySeconds(60 * 60 * 24 * 3);
        return defaultTokenServices;
    }
}

整改后的令牌获取格式

{
    "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiYXV0b2RlbGl2ZXIiXSwiZXhwIjoxNjIxOTM2MzE5LCJ1c2VyX25hbWUiOiJhZG1pbiIsImp0aSI6ImZmNmFiOWJlLTg3YzAtNDI1ZC05MTUxLTIxZmQ1Njc5OGVhOCIsImNsaWVudF9pZCI6ImNsaWVudF9sYWdvdSIsInNjb3BlIjpbImFsbCJdfQ.nmYc1z1guIfoliUPKs9NxGd6GhS95F22yf5DcXgvvSI",
    "token_type": "bearer",
    "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiYXV0b2RlbGl2ZXIiXSwidXNlcl9uYW1lIjoiYWRtaW4iLCJzY29wZSI6WyJhbGwiXSwiYXRpIjoiZmY2YWI5YmUtODdjMC00MjVkLTkxNTEtMjFmZDU2Nzk4ZWE4IiwiZXhwIjoxNjIyMTk1NDk5LCJqdGkiOiJhYmEyMWEwOC02ODNkLTRjM2QtYTY5MC1mZDMyMjVlOGY5ODciLCJjbGllbnRfaWQiOiJjbGllbnRfbGFnb3UifQ.bZdtkWEPrAghIDY4SKLDlgnQQho85qrNbPwg3OfrW2s",
    "expires_in": 19,
    "scope": "all",
    "jti": "ff6ab9be-87c0-425d-9151-21fd56798ea8"
}

2.客户端改造(修改ResourceServerConfig配置)


import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.jwt.crypto.sign.MacSigner;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;

/**
 * @remark
 * @Author pengheng
 * @date 2021/5/25 14:59
 */
@Configuration
@EnableResourceServer //开启资源服务器功能
@EnableWebSecurity //开启web访问安全
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {

    /**
     * 该方法用于定义志愿服务期向远程服务器发起请求,进行token校验等事宜
     *
     * @param resources
     * @throws Exception
     */
    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
//        resources.resourceId("autodeliver");
//        RemoteTokenServices remoteTokenServices = new RemoteTokenServices();
//        //设置校验token的url
//        remoteTokenServices.setCheckTokenEndpointUrl("http://localhost:9999/oauth/check_token");
//        //设置客户端id和客户端密钥
//        remoteTokenServices.setClientId("client_lagou");
//        remoteTokenServices.setClientSecret("abcxyz");

        resources.resourceId("autodeliver").tokenStore(tokenStore()).stateless(true);//无状态设置
    }

    /**
     * 配置API接口认证,设置指定接口过滤或放行
     *
     * @param http
     * @throws Exception
     */
    @Override
    public void configure(HttpSecurity http) throws Exception {
        http
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
                .and()
                .authorizeRequests()
                //指定/autodeliver开头的都要验证
                .antMatchers("/autodeliver/**").authenticated()
                .antMatchers("/demo/**").authenticated()
                .anyRequest().permitAll();

    }

    /**
     * 创建令牌存储对象(内存、数据库、jwt、redis)
     *
     * @return
     */
    public TokenStore tokenStore() {
//        return new InMemoryTokenStore();
        return new JwtTokenStore(jwtAccessTokenConverter());
    }

    private String sign_key = "pengheng";
    /**
     * 返回jwt令牌转换器
     * @return
     */
    public JwtAccessTokenConverter jwtAccessTokenConverter(){
        JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
        jwtAccessTokenConverter.setSigningKey(sign_key);
        jwtAccessTokenConverter.setVerifier(new MacSigner(sign_key));

        return jwtAccessTokenConverter;
    }
}

三、基于数据库加载用户信息+Jwt方式

修改认证中心

  • 创建数据库
DROP DATABASE IF EXISTS `oauth2`;
CREATE DATABASE oauth2;
USE oauth2;

SET NAMES utf8mb4;

SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for oauth_client_details
-- ----------------------------
DROP TABLE IF EXISTS `oauth_client_details`;
CREATE TABLE `oauth_client_details` (
	`client_id` VARCHAR ( 48 ) 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;-- ----------------------------
-- Records of oauth_client_details
-- ----------------------------
BEGIN;

INSERT INTO `oauth_client_details` VALUES ( 'client_lagou123', 'autodeliver,resume', 'abcxyz', 'all', 'password,refresh_token', NULL, NULL, 7200, 259200, NULL, NULL );
COMMIT;

SET FOREIGN_KEY_CHECKS = 1;

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for users
-- ----------------------------
DROP TABLE IF EXISTS `users`;
CREATE TABLE `users` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `username` char(10) DEFAULT NULL,
 `password` char(100) DEFAULT NULL,
 PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of users
-- ----------------------------
BEGIN;
INSERT INTO `users` VALUES (4, 'lagou-user', 'iuxyzds');
COMMIT;
SET FOREIGN_KEY_CHECKS = 1;
  • 引入数据库依赖
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.10</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
        </dependency>	
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
  • 修改application.yml配置引入数据源
spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/oauth2??useUnicode=true&serverTimezone=UTC&characterEncoding=utf-8&useSSL=false&allowMultiQueries=true
    username: root
    password: root
    druid:
      initial-size: 10
      min-idle: 10
      max-active: 30
      max-wait: 50000
  • 认证中心配置类改造
// 注入数据源
@Autowired DataSource dataSource;

@Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        super.configure(clients);
        //客户端信息存储位置 可以在内存中,也可以在数据库中
//        clients.inMemory()
//                //添加客户端配置,指定client_id
//                .withClient("client_lagou")
//                //指定客户端所能访问的密码
//                .secret("abcxyz")
//                //指定客户端所能访问的资源
//                .resourceIds("autodeliver")
//                //认证类型/令牌颁发模式
//                .authorizedGrantTypes("password", "refresh_token")
//                //客户端权限范围
//                .scopes("all");
        clients.withClientDetails(jdbcClientDetailsService());
    }


    @Bean
    public JdbcClientDetailsService jdbcClientDetailsService(){
        return new JdbcClientDetailsService(dataSource);
    }
  • 开发UserDetailsService接⼝的实现类,根据⽤户名从数据库加载⽤户信息

@Service
public class JdbcUserDetailService implements UserDetailsService {
    @Autowired
    private UserRepository userRepository;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        Users users = userRepository.findByUsername(username);
        return new User(users.getUsername(), users.getPassword(), new ArrayList<>());
    }
}

  • 修改SecurityConfig配置


import com.lagou.edu.service.JdbcUserDetailService;
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.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private JdbcUserDetailService jdbcUserDetailService;

    /**
     * 注册⼀个认证管理器对象到容器
     */
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    /**
     * 密码编码对象(密码不进⾏加密处理)
     *
     * @return
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return NoOpPasswordEncoder.getInstance();
    }

    @Autowired
    private PasswordEncoder passwordEncoder;

    /**
     * 处理⽤户名和密码验证事宜
     * 1)客户端传递username和password参数到认证服务器
     * 2)⼀般来说,username和password会存储在数据库中的⽤户表中
     * 3)根据⽤户表中数据,验证当前传递过来的⽤户信息的合法性
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth)
            throws Exception {
        // 在这个⽅法中就可以去关联数据库了,当前我们先把⽤户信息配置在内存中
        // 实例化⼀个⽤户对象(相当于数据表中的⼀条⽤户记录)
//        UserDetails user = new User("admin", "123456", new ArrayList<>());
//        auth.inMemoryAuthentication().withUser(user).passwordEncoder(passwordEncoder);
        //数据库校验用户信息
        auth.userDetailsService(jdbcUserDetailService);
    }
}

四、token添加自定义信息

1.认证中心配置

重写AccessTokenConverter

@Component
public class CustomAccessTokenConverter extends DefaultAccessTokenConverter {

    @Override
    public Map<String, ?> convertAccessToken(OAuth2AccessToken token, OAuth2Authentication authentication) {
        Map<String, Object> stringMap = (Map<String, Object>) super.convertAccessToken(token, authentication);
        stringMap.put("hello", ThreadLocalRandom.current().nextInt(10));
        return stringMap;
    }
}	

OauthServerConfig配置添加自定义转换器

@Autowired
private CustomAccessTokenConverter customAccessTokenConverter;
/**
 * 返回jwt令牌转换器
 * @return
 */
public JwtAccessTokenConverter jwtAccessTokenConverter(){
    JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
    jwtAccessTokenConverter.setSigningKey(sign_key);
    jwtAccessTokenConverter.setVerifier(new MacSigner(sign_key));

    //设置请求token转换器
    jwtAccessTokenConverter.setAccessTokenConverter(customAccessTokenConverter);
    return jwtAccessTokenConverter;
}

2.资源服务器配置

重写CustomAccessTokenConverter

@Component
public class CustomAccessTokenConverter extends DefaultAccessTokenConverter {

    @Override
    public OAuth2Authentication extractAuthentication(Map<String, ?> map) {
        OAuth2Authentication oAuth2Authentication = super.extractAuthentication(map);
        oAuth2Authentication.setDetails(map);
        return oAuth2Authentication;
    }
}

获取Token存放内容方法

Object details = SecurityContextHolder.getContext().getAuthentication().getDetails();
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值