【SpringBoot】自从集成spring-security-oauth2后,实现统一认证授权so easy!

news2024/11/24 12:59:39

文章目录

  • 一.前言
    • 1.principal和credential的区别
    • 2.生成私钥公钥
    • 3.用户认证分析
    • 4.认证解决方案
      • 4.1.单点登录
      • 4.2.第三方账号登录
      • 4.3.单token系统和双token系统业务逻辑
  • 二.Spring Security
    • 1.快速入门
    • 2.基本原理
        • FilterSecurityInterceptor
        • ExceptionTranslationFilter
        • UsernamePasswordAuthenticationFilter
    • 3.基于数据库中实现用户认证/授权
  • 三.Spring Security集成 OAuth2
  • 1.搭建授权服务器
    • 1.0.授权服务器简介
    • 1.1.引入依赖
    • 1.2.application.yaml
    • 1.3.Oauth2授权服务配置
    • 1.4.SpringSecurity配置
    • 1.5.启动类
      • 1.测试授权码模式
      • 2.测试密码模式
  • 2.搭建资源服务器
    • 1.0.前言
    • 1.1.引入依赖
    • 1.2.application.yaml
    • 1.3.oauth2资源服务配置
    • 1.4.请求测试类
    • 1.5.启动类开启Oauth2
      • 1.5.1.授权服务器和资源服务联合测试
  • 3.授权服务器改成动态数据库查询的方式
    • 1.1.下载oauth2官方建表语句
    • 1.2.新增依赖
    • 1.3./修改授权服务器
      • 1.修改配置文件
      • 2.修改AuthorizationServerConfig
      • 4.测试
  • 四.4种授权模式
    • 1.授权码模式
    • 2.密码模式
      • 2.1.密码模式与授权码模式区别
    • 3.简化模式
    • 4.客户端模式
  • 五.Spring Security OAuth2集成JWT
    • 1.生成JTW
    • 2、自定义Payload

一.前言

1.principal和credential的区别

  • principal:能唯一标识用户身份的属性,一个用户可以有多个principal

    • 如登录的唯一标识,用户可以使用用户名或手机或邮箱进行登录,这些principal是让别人知道的
  • credential:凭证,用户才知道的,简单的说就是密码

    • 如:手机开锁,可以使用屏幕密码也可以使用人脸识别,屏幕密码和人脸是你个人(用户)才拥有的;

    • principals 和 credentials 组合就是用户名 / 密码了。

2.生成私钥公钥

  1. 使用命令生成密钥证书,采用RSA 算法,每个证书包含公钥和私钥
    • Keytool 是一个java提供的证书管理工具
      keytool -genkeypair -alias oyjp -keyalg RSA -keypass ouyangjianpeng -keystore ouyangjianpeng.jks -storepass ouyangjianpeng 
      
      -alias:密钥的别名 
      -keyalg:使用的hash算法 
      -keypass:密钥的访问密码 
      -keystore:密钥库文件名,changgou.jks保存了生成的证书 
      -storepass:密钥库的访问密码 
      
    • 成功后会在当前目录生成一个ouyangjianpeng.jks文件
  2. 查询证书信息:
    keytool -list -keystore ouyangjianpeng.jks
    
    在这里插入图片描述

3.导出公钥

  • openssl是一个加解密工具包,这里使用openssl来导出公钥信息。

    • 安装 openssl:http://slproweb.com/products/Win32OpenSSL.html

      • 安装资料目录下的Win64OpenSSL-1_1_1b.exe
    • cmd进入ouyangjianpeng.jks文件所在目录执行如下命令:

      keytool -list -rfc --keystore ouyangjianpeng.jks | openssl x509 -inform pem -pubkey
      

      在这里插入图片描述

3.用户认证分析

4.认证解决方案

4.1.单点登录

单点登录(Single Sign On),简称为 SSO,是目前比较流行的企业业务整合的解决方案之一。 SSO的定义是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统

  • 分布式系统要实现单点登录,通常将认证系统独立抽取出来,并且将用户信息存储在单独数据库中,比如 MySQL、Redis,考虑性能要求,通常存储在Redis中,如下图:
    在这里插入图片描述
    Java中有很多用户认证的框架都可以实现单点登录:

    Apache Shiro. 
    CAS 
    Spring security   
    

4.2.第三方账号登录

所谓的第三方登录,是说基于用户在第三方平台上已有的账号和密码来实现应用的登录或者注册的功能。而第三方平台一般是已拥有大量用户的平台,国外的比如Facebook,Twitter等,国内的比如微博、微信、QQ等。
在这里插入图片描述

​ 第三方认证技术方案最主要是 解决认证协议的通用标准问题,因为要实现跨系统认证各系统之间要遵循一定的
接口协议。

  • Oauth协议为用户资源的授权提供了一个安全的、开放而又简易的标准。同时,任何第三方都可以使用Oauth认
    证服务,任何服务提供商都可以实现自身的OAUTH认证服务,因而Oauth是开放的。

  • Oauth协议目前发展到2.0版本,1.0版本过于复杂,2.0版本已得到广泛应用。

使用QQ认证的过程:
在这里插入图片描述

  1. 用户访问网站A,网站A请求QQ授权服务器发送授权请求给用户
  2. 用户确认授权后,返回授权码给第三方
  3. 第三方拿到授权码后,携带授权码向QQ授权服务器申请令牌
  4. 第三方拿到令牌后,携带令牌向QQ请求用户的基本信息
  5. QQ资源服务器根据访问令牌,返回给第三方用户的基本信息

4.3.单token系统和双token系统业务逻辑

在这里插入图片描述


在这里插入图片描述

二.Spring Security

Spring Security是一个用于快速实现认证/授权的安全框架。

  • 认证(Authentication) :即校验用户的身份信息是否合法的过程,合法方可继续访问,不合法则拒绝访问。常见的认证方式有:用户名密码登录,二维码登录,手机短信登录,指纹认证等方式。

  • 授权(Authorization): 即是Authentication认证通过后根据用户的权限来控制用户是否能访问指定资源,拥有该资源的访问权限则正常访问,没有权限则拒绝访问。

1.快速入门

引入依赖

		<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-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-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <scope>test</scope>
        </dependency>

编写controller

@RestController
@RequestMapping("/test")
public class TestController {
    @GetMapping("/hello")
    public String add(){
        return "hello security";
    }
}

访问页面时出现了默认的安全认证窗口,密码在控制台处进行显示。默认的用户名为user
在这里插入图片描述

2.基本原理

SpringSecurity本质上是一个过滤器链。常用过滤器如下

FilterSecurityInterceptor

  • 位于最底端的,是一个方法级权限过滤器
public class FilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter {
    private static final String FILTER_APPLIED = "__spring_security_filterSecurityInterceptor_filterApplied";
    private FilterInvocationSecurityMetadataSource securityMetadataSource;
    private boolean observeOncePerRequest = true;
}

ExceptionTranslationFilter

  • 异常处理过滤器,用来处理在认证授权的过程中抛出的异常信息
public class ExceptionTranslationFilter extends GenericFilterBean implements MessageSourceAware {
    private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
        try {
            chain.doFilter(request, response);
        } catch (IOException var7) {
            throw var7;
        } catch (Exception var8) {
            Throwable[] causeChain = this.throwableAnalyzer.determineCauseChain(var8);
            RuntimeException securityException = (AuthenticationException)this.throwableAnalyzer.getFirstThrowableOfType(AuthenticationException.class, causeChain);
            if (securityException == null) {
                securityException = (AccessDeniedException)this.throwableAnalyzer.getFirstThrowableOfType(AccessDeniedException.class, causeChain);
            }

            if (securityException == null) {
                this.rethrow(var8);
            }

            if (response.isCommitted()) {
                throw new ServletException("Unable to handle the Spring Security Exception because the response is already committed.", var8);
            }

            this.handleSpringSecurityException(request, response, chain, (RuntimeException)securityException);
        }
    }

UsernamePasswordAuthenticationFilter

  • /login的POST请求做拦截,校验表单中用户名密码

核心的业务逻辑如下

public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        if (this.postOnly && !request.getMethod().equals("POST")) {
            throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
        } else {
            String username = this.obtainUsername(request);
            username = username != null ? username.trim() : "";
            String password = this.obtainPassword(request);
            password = password != null ? password : "";
            UsernamePasswordAuthenticationToken authRequest = UsernamePasswordAuthenticationToken.unauthenticated(username, password);
            this.setDetails(request, authRequest);
            return this.getAuthenticationManager().authenticate(authRequest);
        }
    }

3.基于数据库中实现用户认证/授权

WebSecurityConfigurerAdapter类

  • 引入springSecurity的依赖并做相关的配置,对资源进行保护。通常需要重写以下方法:
//可以配置忽略某些请求。
public void configure(WebSecurity web) throws Exception {}

//配置安全拦截机制控制资源的访问,可配置匹配哪些请求、哪些可以直接访问、哪些需要授权后访问。 
protected void configure(HttpSecurity http) throws Exception {}

//配置验证的用户信息源和密码加密策略,并向容器注入AuthenticationManager对象,这需要在OAuth2中配置(授权服务器),配置了AuthenticationManager密码验证才会生效。 
public void configure(AuthenticationManagerBuilder auth) throws Exception {}

//配置验证管理的Bean 
public AuthenticationManager authenticationManagerBean() throws Exception {}

新增依赖

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.2</version>
        </dependency>
 
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
       </dependency>

自定义UserDetail类(伪代码)

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.blb.java11springsecurity.entity.User;
import com.blb.java11springsecurity.service.IUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
 
import java.util.List;
 
/**
 * 实现自定义用户登录逻辑
 */
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
 
    @Autowired
    private IUserService userService;//用户表实现类
 
    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
        //按用户名查询用户信息
        User user = userService.getOne(new QueryWrapper<User>().lambda().eq(User::getUsername, s));
        if(user == null){
            throw new UsernameNotFoundException("用户名不存在");
        }
        //查询所有用户权限 List<String> --> xx,xxx,xxx,xx --> List<Authory>
        List<String> authList = userService.getAuthoritiesByUsername(s);
        String auths = String.join(",", authList);
        //把用户信息包装到UserDetails的实现类User中
        return new org.springframework.security.core.userdetails.User(user.getUsername(),user.getPassword(),
                AuthorityUtils.commaSeparatedStringToAuthorityList(auths));
    }
}

SpringSecurity的核心配置(伪代码)

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
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.bcrypt.BCryptPasswordEncoder;
//启动Security的验证
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private BCryptPasswordEncoder bCryptPasswordEncoder;
 
    @Autowired
    private UserDetailsService userDetailsService;//引入自定义用户登录逻辑
 
    //配置验证用户的账号和密码
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    //使用内存中用户信息进行认证
		/*auth.inMemoryAuthentication()
                .withUser("zhangsan")
                .password(bCryptPasswordEncoder.encode("123"))
                .roles("ADMIN","USER") //角色
                .and()
                .withUser("lisi")
                .password(bCryptPasswordEncoder.encode("123"))
                .roles("USER"); //权限
	*/
    //使用自定义UserDetail类定义逻辑进行认证
        auth.userDetailsService(userDetailsService);
    }
 
    //配置访问控制
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //给请求授权
        http.authorizeRequests()
                //给登录相关的请求放行(permitAll即未登陆,定义的路径依旧不会拦截)
                .antMatchers("/login", "/login.html", "/**/*.css").permitAll()
                //访问控制(即使登陆了,访问路径需要相应的权限)
                .antMatchers("/admin/**").hasAuthority("销售管理")
                .antMatchers("/user/**").hasAuthority("采购管理")
                //其他路径进行权限验证,说白了就是拦截
                .anyRequest().authenticated()
                .and()
                //配置自定义登录
                .formLogin()
                .loginPage("/login.html") //登录页面
                .loginProcessingUrl("/login") //处理登录的url
                .successForwardUrl("/hello.html") //登录成功后跳转的url
        ;
    }
     
    //提供密码编码器,采用加密,加盐策略。每次加密后结果都不一样。贼安全
    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

三.Spring Security集成 OAuth2

  • OAuth2和Spring Security集成,就可以得到一套完整的安全解决方案。然后通过Spring Security OAuth2构建一个授权服务器来验证用户身份以提供access_token,并使用这个access_token来从资源服务器请求数据。

1.搭建授权服务器

1.0.授权服务器简介

搭建授权服务器最重要的是:继承AuthorizationServerConfigurerAdapter接口并在实现类上加注解@EnableAuthorizationServer标识这是一个授权服务器。

  • AuthorizationServerConfigurerAdapter接口有三个方法
    public class AuthorizationServerConfigurerAdapter implements AuthorizationServerConfigurer {
        //配置客户端信息,即怎么读取用户信息 是基于内存还是jdbc
    	@Override
    	public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
    	}
     	 //用来配置令牌(token)的访问端点和令牌服务(token services)。
    	@Override
    	public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
    	}
     	//用来配置令牌端点(Token Endpoint)的安全约束。
    	@Override
    	public void configure(AuthorizationServerSecurityConfigurer security) throws Exception{
    	}
    }
    

ClientDetailsServiceConfigurer:配置客户端信息

  • 可以使用内存或JDBC来实现客户端详情服务, ClientDetails有如下几个重要属性:
        clients.inMemory()
                //客户端id
                .withClient("client_1")
                //客户端secret
                .secret(passwordEncoder().encode("123456"))
                //回调地址
                .redirectUris("https://www.baidu.com/")
                /* OAuth2为我们提供了四种授权方式:
                 * 1、授权码模式(authorization code)用在客户端与服务端应用之间授权
                 * 2、简化模式(implicit)用在移动app或者web app(这些app是在用户的设备上的,如在手机上调起微信来进行认证授权)
                 * 3、密码模式(resource owner password credentials)应用直接都是受信任的(都是由一家公司开发的)
                 * 4、客户端模式(client credentials)用在应用API访问
                 */
                .authorizedGrantTypes("password", "client_credentials", "refresh_token", "authorization_code")
                //授权范围,默认为空,表示客户端拥有全部的访问范围。
                .scopes("all")
                //accessToken有效期
                .accessTokenValiditySeconds(accessTokenValiditySeconds)
                //refreshToken有效期
                .refreshTokenValiditySeconds(refreshTokenValiditySeconds);

AuthorizationServerEndpointsConfigurer:配置令牌访问端点

  • 使用@EnableAuthorizationServer注解后,应用启动后将自动生成几个Endpoint:

    /oauth/authorize:验证
    /oauth/token:获取token
    /oauth/confirm_access:用户授权
    /oauth/error:认证失败
    /oauth/check_token:资源服务器用来校验token
    /oauth/token_key:如果使用JWT令牌,则公开用于令牌验证的公钥
    

AuthorizationServerSecurityConfigurer:用来配置令牌端点(Token Endpoint)的安全约束

	上面的/oauth/check_token和/oauth/token_key端点,他们都是用于检查令牌的,默认受保护denyAll()。
	使用tokenKeyAccess()checkTokenAccess()方法会打开这些端点以供使用,如:
        // 允许check_token访问
        oauthServer.checkTokenAccess("permitAll()");
        // 允许表单认证
        oauthServer.allowFormAuthenticationForClients();

Token存储方式

OAuth2存储token值的方式由多种,所有的实现方式都是实现了TokenStore接口
	InMemoryTokenStore:  存储在本机内存
	JdbcTokenStore: 存储在数据库
	JwtTokenStore:  不会存储到任何介质中
	RedisTokenStore: 存储在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 :存储授权成功的客户端信息。

1.1.引入依赖

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

或者 引入spring cloud oauth2依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
 
<!-- spring cloud -->
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>Hoxton.SR8</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

完整如下:

<?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.2.RELEASE</version>
        <relativePath/>
    </parent>
   
    <groupId>com.oyjp</groupId>
    <artifactId>spring-boot-oauth2-server</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Hoxton.RELEASE</spring-cloud.version>
    </properties>

    <!-- spring cloud -->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Hoxton.SR8</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>


    <dependencies>
        <!-- SpringBoot整合Web组件 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-->spring-boot 整合security -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-oauth2</artifactId>
        </dependency>

        <!-- springboot整合freemarker -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-freemarker</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

1.2.application.yaml

server:
  port: 8080

spring:
  application:
    name: oauth2-center-server

logging:
  level:
    root: info
    org.springframework.security: DEBUG

1.3.Oauth2授权服务配置

具体实现

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.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
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.provisioning.InMemoryUserDetailsManager;

//配置授权中心信息
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
    /**
     * accessToken有效期 两小时
     */
    private int accessTokenValiditySeconds = 7200;
 
    /**
     * refreshToken有效期 两小时
     */
    private int refreshTokenValiditySeconds = 7200;
 
    /**
     * 添加商户信息
     * @param clients 客户端即可
     * @throws Exception 异常
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
           		//配置client_id
                .withClient("client_1")
                //配置client-secret
                .secret(passwordEncoder().encode("123456"))
                //配置redirect_uri,用于授权成功后跳转
                .redirectUris("https://www.baidu.com/")
                 //配置grant_type,表示授权类型
                /* OAuth2为我们提供了四种授权方式:
                 * 1、授权码模式(authorization code)用在客户端与服务端应用之间授权
                 * 2、简化模式(implicit)用在移动app或者web app(这些app是在用户的设备上的,如在手机上调起微信来进行认证授权)
                 * 3、密码模式(resource owner password credentials)应用直接都是受信任的(都是由一家公司开发的)
                 * 4、客户端模式(client credentials)用在应用API访问
                 */
  				.authorizedGrantTypes("password", "client_credentials", "refresh_token", "authorization_code", "implicit")
                //授权范围
                .scopes("all")
                //配置访问token的有效期
                .accessTokenValiditySeconds(accessTokenValiditySeconds)
                //配置刷新token的有效期
                .refreshTokenValiditySeconds(refreshTokenValiditySeconds);
    }

    /**
     * 设置token类型
     * @param endpoints
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
        endpoints.authenticationManager(authenticationManager())
                .allowedTokenEndpointRequestMethods(HttpMethod.GET,HttpMethod.POST); //支持GET,POST请求
        endpoints.userDetailsService(userDetailsService());
    }
 
    @Override
    public void configure(AuthorizationServerSecurityConfigurer oauthServer) {
        // 允许表单认证
        oauthServer.allowFormAuthenticationForClients();
        // 允许check_token访问
        oauthServer.checkTokenAccess("permitAll()");
    }
 
    @Bean
    AuthenticationManager authenticationManager() {
        AuthenticationManager authenticationManager = new AuthenticationManager() {
            @Override
            public Authentication authenticate(Authentication authentication) throws AuthenticationException {
                return daoAuhthenticationProvider().authenticate(authentication);
            }

        };
        return authenticationManager;
    }
 
    @Bean
    public AuthenticationProvider daoAuhthenticationProvider() {
        DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
        daoAuthenticationProvider.setUserDetailsService(userDetailsService());
        daoAuthenticationProvider.setHideUserNotFoundExceptions(false);
        daoAuthenticationProvider.setPasswordEncoder(passwordEncoder());
        return daoAuthenticationProvider;
    }
 
    /**
     * 设置添加用户信息,正常应该从数据库中读取
     * @return UserDetailsService
     */
    @Bean
    UserDetailsService userDetailsService() {
        InMemoryUserDetailsManager userDetailsService = new InMemoryUserDetailsManager();
        userDetailsService.createUser(User.withUsername("user_1").password(passwordEncoder().encode("123456"))
                .authorities("ROLE_USER").build());
        userDetailsService.createUser(User.withUsername("user_2").password(passwordEncoder().encode("1234567"))
                .authorities("ROLE_USER").build());
        return userDetailsService;
    }
 
    /**
     * 采用BCryptPasswordEncoder对密码进行编码
     * @return PasswordEncoder
     */
    @Bean
    PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

1.4.SpringSecurity配置

import org.springframework.context.annotation.Bean;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
/**
 *  添加Security权限配置
 */
@Component
public class SecurityConfig  extends WebSecurityConfigurerAdapter {
     /***
     * 忽略安全拦截的URL
     * @param web
     * @throws Exception
     */
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/oauth/**","/login.html","/css/**","/data/**","/fonts/**","/img/**","/js/**");
    }

    /**
     *  授权中心管理器
     * @return AuthenticationManager
     * @throws Exception 异常
     */
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
 
    //@Bean
    //public PasswordEncoder passwordEncoder() {
    //    return new BCryptPasswordEncoder();
    //}
 
    /**
     *  拦截所有请求,使用httpBasic方式登陆
     * @param http 请求
     * @throws Exception 异常
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().antMatchers("/**").fullyAuthenticated().and().httpBasic();
    }

}

1.5.启动类

@SpringBootApplication
public class Oauth2AuthorizationCenterApplication {
    public static void main(String[] args) {
        SpringApplication.run(Oauth2AuthorizationCenterApplication.class, args);
    }
}

1.测试授权码模式

  1. 请求 http://localhost:8080/oauth/authorize?response_type=code&client_id=client_1&redirect_uri=https://www.baidu.com/

    • 授权服务器将携带授权码code然后转发至redirect_uri=https://www.baidu.com/然
      在这里插入图片描述
  2. 使用授权码获取accessToken
    POST请求 http://localhost:8080/oauth/token?grant_type=authorization_code&client_id=client_1&client_secret=123456&code=EwaTib&redirect_uri=https://www.baidu.com/&scope=all
    在这里插入图片描述

2.测试密码模式

POST请求 http://localhost:8080/oauth/token?grant_type=password&client_id=client_1&client_secret=123456&code=YerSeW&scope=all&username=user_1&password=123456
在这里插入图片描述

2.搭建资源服务器

1.0.前言

搭建资源服务器最重要的是:继承ResourceServerConfigurerAdapter接口并在实现类上加注解@EnableResourceServer标识这是一个资源服务器。

  • 该接口只有两个方法

    public class ResourceServerConfigurerAdapter implements ResourceServerConfigurer {
     	//配置哪些可以访问,哪些不可访问,默认情况下所有不在/oauth/**下的资源都是受保护的资源
    	@Override
    	public void configure(HttpSecurity http) throws Exception {
    		http.authorizeRequests().anyRequest().authenticated();
    	}
    	
    	//它可以为资源服务器添加一个特定的属性,如resourceId,也就是说我们一个授权服务器可能对应多个资源服务器,
    	//可以为每个资源服务器添加一个resourceId属性进行区分,可选,但建议使用,如果存在,auth服务器将进行验证。
    	@Override
    	public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
    	}
    }
    

1.1.引入依赖

<?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.2.RELEASE</version>
        <relativePath/>
    </parent>
   
    <groupId>com.oyjp</groupId>
    <artifactId>spring-boot-oauth2-server</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Hoxton.RELEASE</spring-cloud.version>
    </properties>

    <!-- spring cloud -->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Hoxton.SR8</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>


    <dependencies>
        <!-- SpringBoot整合Web组件 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-->spring-boot 整合security -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-oauth2</artifactId>
        </dependency>

        <!-- springboot整合freemarker -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-freemarker</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

1.2.application.yaml

server:
  port: 8081


spring:
  application:
    name: oauth2-resource-server

security:
  oauth2:
    resource:
      ####从认证授权中心上验证token
      tokenInfoUri: http://localhost:8080/oauth/check_token
      preferTokenInfo: true
    client:
      accessTokenUri: http://localhost:8080/oauth/token
      userAuthorizationUri: http://localhost:8080/oauth/authorize
      ###appid
      clientId: client_1
      ###appSecret
      clientSecret: 123456

logging:
  level:
    root: info
    org.springframework.security: DEBUG

1.3.oauth2资源服务配置

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;

/**
 * @Description: 资源拦截配置
 */
@Configuration
@EnableResourceServer
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
    @Override
    public void configure(HttpSecurity http) throws Exception {
        // 对 api/order 请求进行拦截
        http.authorizeRequests().antMatchers("/api/test/**").authenticated();
    }
}

1.4.请求测试类


@RestController
@RequestMapping("/api/test")
public class TestController {
    @RequestMapping("/add")
    public String addOrder() {
        return "add success!";
    }
}

1.5.启动类开启Oauth2

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.security.oauth2.client.EnableOAuth2Sso;
@SpringBootApplication
@EnableOAuth2Sso
public class Oauth2ResourceServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(Oauth2ResourceServerApplication.class, args);
    }

}

1.5.1.授权服务器和资源服务联合测试

  1. 没授权时请求http://127.0.0.1:8081/api/test/add
    在这里插入图片描述
  2. 授权时请求http://127.0.0.1:8081/api/test/add
  • 先获取token(密码模式)

    • http://localhost:8080/oauth/token?grant_type=password&client_id=client_1&client_secret=123456&code=YerSeW&scope=all&username=user_1&password=123456
      在这里插入图片描述
  • 使用token访问资源服务器
    在这里插入图片描述

3.授权服务器改成动态数据库查询的方式

1.1.下载oauth2官方建表语句

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 :存储授权成功的客户端信息

在这里插入图片描述

1.2.新增依赖

       <!-- mysql -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
 
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>

1.3./修改授权服务器

1.修改配置文件

server:
  port: 8080


spring:
  application:
    name: oauth2-center-server
    
  datasource:
    hikari:
      connection-test-query: SELECT 1
      minimum-idle: 1
      maximum-pool-size: 5
      pool-name: dbcp1
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/oauth2_test?autoReconnect=true&useSSL=false&serverTimezone=Asia/Shanghai
    username: root
    password: root


logging:
  level:
    root: info
    org.springframework.security: DEBUG

2.修改AuthorizationServerConfig

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
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.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
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;
import org.springframework.security.oauth2.provider.token.store.JdbcTokenStore;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import javax.sql.DataSource;

/**
 * @Description: 配置授权中心信息
 */
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
 
    //数据源,用于从数据库获取数据进行认证操作,测试可以从内存中获取
    @Autowired
    @Qualifier("dataSource")
    private DataSource dataSource;
 
 
    /**
     * accessToken有效期 两小时
     */
    private int accessTokenValiditySeconds = 7200;
 
    /**
     * refreshToken有效期 两小时
     */
    private int refreshTokenValiditySeconds = 7200;
 
    @Bean
    public TokenStore tokenStore() {
        // return new InMemoryTokenStore(); //使用内存中的 token store
        return new JdbcTokenStore(dataSource); /// 使用Jdbctoken store
    }
 
 
    /**
     * 添加商户信息
     *
     * @param clients 商户
     * @throws Exception 异常
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.jdbc(dataSource)
                //测试首次运行可以指定测试数据,如果数据库中没有则不报错,如果有或者第二次运行会报错,因为数据库已经存在了,需要注释掉
                .withClient("client_1")
                //商户secret
                .secret(passwordEncoder().encode("123456"))
                //回调地址
                .redirectUris("https://www.baidu.com/")
                /* OAuth2为我们提供了四种授权方式:
                 * 1、授权码模式(authorization code)用在客户端与服务端应用之间授权
                 * 2、简化模式(implicit)用在移动app或者web app(这些app是在用户的设备上的,如在手机上调起微信来进行认证授权)
                 * 3、密码模式(resource owner password credentials)应用直接都是受信任的(都是由一家公司开发的)
                 * 4、客户端模式(client credentials)用在应用API访问
                 */
  				.authorizedGrantTypes("password", "client_credentials", "refresh_token", "authorization_code", "implicit")
                //授权范围
                .scopes("all")
                //accessToken有效期
                .accessTokenValiditySeconds(accessTokenValiditySeconds)
                //refreshToken有效期
                .refreshTokenValiditySeconds(refreshTokenValiditySeconds);
    }
 
    /**
     * 设置token类型
     * @param endpoints
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
        endpoints.authenticationManager(authenticationManager())
                .allowedTokenEndpointRequestMethods(HttpMethod.GET,HttpMethod.POST);
        endpoints.userDetailsService(userDetailsService());
    }
 
    @Override
    public void configure(AuthorizationServerSecurityConfigurer oauthServer) {
        // 允许表单认证
        oauthServer.allowFormAuthenticationForClients();
        // 允许check_token访问
        oauthServer.checkTokenAccess("permitAll()");
    }
 
    @Bean
    AuthenticationManager authenticationManager() {
        AuthenticationManager authenticationManager = new AuthenticationManager() {
            @Override
            public Authentication authenticate(Authentication authentication) throws AuthenticationException {
                return daoAuhthenticationProvider().authenticate(authentication);
            }
 
        };
        return authenticationManager;
    }
 
    @Bean
    public AuthenticationProvider daoAuhthenticationProvider() {
        DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
        daoAuthenticationProvider.setUserDetailsService(userDetailsService());
        daoAuthenticationProvider.setHideUserNotFoundExceptions(false);
        daoAuthenticationProvider.setPasswordEncoder(passwordEncoder());
        return daoAuthenticationProvider;
    }
 
    /**
     * 设置添加用户信息,正常应该从数据库中读取
     *
     * @return UserDetailsService
     */
    @Bean
    UserDetailsService userDetailsService() {
        InMemoryUserDetailsManager userDetailsService = new InMemoryUserDetailsManager();
        userDetailsService.createUser(User.withUsername("user_1").password(passwordEncoder().encode("123456"))
                .authorities("ROLE_USER").build());
        userDetailsService.createUser(User.withUsername("user_2").password(passwordEncoder().encode("1234567"))
                .authorities("ROLE_USER").build());
        return userDetailsService;
    }
 
    /**
     * 设置加密方式
     *
     * @return PasswordEncoder
     */
    @Bean
    PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

4.测试

在这里插入图片描述

检查数据库发现测试商户已经导入到数据库了
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

四.4种授权模式

OAuth2为我们提供了四种授权方式:

  1. 授权码模式(authorization_code)用在客户端与服务端应用之间授权
  2. 简化模式(implicit)用在移动app或者web app(这些app是在用户的设备上的,如:在手机上调起微信来进行认证授权)
  3. 密码模式(password)应用直接都是受信任的(都是由一家公司开发的)
  4. 客户端模式(client_credentials)用在应用API访问

1.授权码模式

  1. 客户端请求第三方授权,将重定向到授权服务器,重定向时会附加客户端信息,然后客户端要求用户给予授权,如:

    • http://localhost:8080/oauth/authorize?response_type=code&client_id=client_1&redirect_uri=https://www.baidu.com/

      • 参数列表如下:

        client_id:客户端标识。
        response_code:授权码模式固定为code。
        scope:客户端权限范围。
        redirect_uri:跳转uri,当授权码申请成功后会跳转到此地址,并在后边带上code参数。
        
  2. 浏览器出现向授权服务器授权页面,用户同意给客户端授权。

  3. 授权服务器将授权码发送给客户端通过redirect_uri携带code

  4. 客户端拿着授权码想授权服务器申请token

    • http://localhost:8080/oauth/token?grant_type=authorization_code&client_id=client_1&client_secret=123456&code=iZ81fR&redirect_uri=https://www.baidu.com/&scope=all

    • 参数列表如下:

      client_id:客户端标识。
      client_secret:客户端密钥。
      grant_type:授权类型,填写authorization_code。
      code:授权码,授权码只使用一次就失效。
      redirect_uri:申请授权码时的跳转url。
      
  5. 授权服务器返回令牌(access_token

  6. 客户端请求资源服务器的资源,资源服务校验令牌合法性,完成授权

  7. 资源服务器返回受保护资源

总结:

  • 这种模式是四种模式中最安全的,一般用于client是Web服务器端应用或第三方App调用资源服务的时候,因为在这种模式中access_token不经过浏览器或者移动端的App,而是直接从服务端去交换,这样就最大限度的减少了令牌泄露的风险。

测试

  1. 请求认证服务获取授权码

    • http://localhost:8080/oauth/authorize?response_type=code&client_id=client_1&redirect_uri=https://www.baidu.com/
  2. 跳转到登录页面: 输入账号:user_1 密码:123456,登录进入授权页面:
    在这里插入图片描述

  3. 进入授权页面
    在这里插入图片描述

  4. 点击Authorize,确认授权后, 认证服务携带授权码跳转redirect_uri,code=pfiYeD就是返回的授权码, 每一个授权码只能使用一次
    https://www.baidu.com/?code=pfiYeD
    在这里插入图片描述

  5. 使用该code申请令牌
    http://localhost:8080/oauth/token?grant_type=authorization_code&client_id=client_1&client_secret=123456&code=pfiYeD&redirect_uri=https://www.baidu.com/&scope=all
    在这里插入图片描述

可能你会遇到返回没有refresh_token的情况,检查看下客户端配置authorizedGrantTypes是否支持refresh_token,我上面配置了才会返回,如下:

.authorizedGrantTypes("authorization_code", "client_credentials", "password", "implicit", "refresh_token")

2.密码模式

步骤一

  • 资源拥有者将用户名、密码发送给客户端。

步骤二

  • 客户端拿着资源拥有者的用户名、密码向授权服务器请求令牌,如下:

  • http://localhost:8080/oauth/token?grant_type=password&client_id=client_1&client_secret=123456&code=YerSeW&scope=all&username=user_1&password=123456

    • 参数列表如下:
      client_id:客户端标识。
      client_secret:客户端密钥。
      grant_type:授权类型,填写password。
      username:资源拥有者用户名。
      password:资源拥有者密码。
      

步骤三

  • 授权服务器将令牌发送给client。

总结

  • 这种模式十分简单,但直接将用户敏感信息泄露给了client,因此这就说明这种模式只能用于client是我们自己开发的情况下。

测试请求令牌,如下:
在这里插入图片描述

2.1.密码模式与授权码模式区别

  • 授权码模式申请授权码的过程是用户直接与认证服务器进行交互,然后授权结果由认证服务器告知第三方客户端,也就是不会向第三方客户端暴露服务提供商的用户密码信息

  • 密码模式,是用户将用户密码信息交给第三方,然后由第三方向服务提供商进行认证和资源请求。

    • 绝大多数的服务提供商都会选择使用授权码模式,避免自己的用户密码暴漏给第三方。所以密码模式只适用于服务提供商对第三方厂商高度信任的情况下才能使用。
  • 客户端模式、简化模式的应用很少

3.简化模式

步骤一

  • 资源拥有者打开客户端,客户端要求资源拥有者给预授权,它将浏览器重定向到授权服务器,重定向时会附加客户端的身份信息,如:

    • http://localhost:8080/oauth/authorize?client_id=client_1&response_type=token&scope=all&redirect_uri=https://www.baidu.com/
      • 参数同授权码模式,将response_type改为token即可。

步骤二

  • 浏览器出现向授权服务器授权页面,之后用户同意授权。

步骤三

  • 授权服务器将授权码和令牌到重定向uri之后。

测试

  • 浏览器访问认证页面:

    • http://localhost:8080/oauth/authorize?client_id=client_1&response_type=token&scope=all&redirect_uri=https://www.baidu.com/

在这里插入图片描述

  • 输入账号:user_1 密码:123456,登录进入授权页面:

在这里插入图片描述

  • 确认授权后,浏览器会重定向到指定的redirect_uri路径,并将token存放在uri路径之后。

在这里插入图片描述

  • https://www.baidu.com/#access_token=c2535eca-d9b5-4176-830b-aea1fa2ccf4c&token_type=bearer&expires_in=7199

4.客户端模式

步骤一

  • 客户端向授权服务器发送自己的身份信息,并请求令牌。

步骤二

  • 确认客户端身份无误后,将令牌发送给client,请求如下:

    • http://localhost:8080/oauth/token?client_id=client_1&client_secret=123456&grant_type=client_credentials&scopes=all

    • 参数列表如下:

      client_id:客户端标识。
      client_secret:客户端密钥。
      grant_type:授权类型,填写client_credentials。
      

总结:

  • 这种模式是最方便但最不安全的模式。因此这就要求我们对client完全信任,而client本身也是安全的,因此这种模式一般用来提供给我们完全信任的服务端使用

测试请求令牌,如下:

在这里插入图片描述

五.Spring Security OAuth2集成JWT

1.生成JTW

  1. 修改tokenStor为JwtTokenStore
    //    //令牌持久化存储接口
    @Bean
    public TokenStore tokenStore() {
        // return new InMemoryTokenStore(); //使用内存中的 token store
        // return new JdbcTokenStore(dataSource); /// 使用Jdbctoken store
        return new JwtTokenStore(accessTokenConverter());

    }
    //jwt令牌转换器
    @Bean
    public JwtAccessTokenConverter accessTokenConverter() {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setSigningKey("abc123");
        return converter;
    }
  1. 设置AuthorizationServerEndpointsConfigurer的tokenStore、tokenEnhancer
   //端点设置自定义token存储,token增强
   endpoints.tokenStore(tokenStore()).tokenEnhancer(accessTokenConverter());
  1. 使用密码模式访问
  • http://localhost:8080/oauth/token?grant_type=password&client_id=client_1&client_secret=123456&code=YerSeW&scope=all&username=user_1&password=123456
    • 解析access_token

在这里插入图片描述

2、自定义Payload

将自定义的TokenEnhancer加入到TokenEnhancerChain中,最后设置到端点中endpoints.tokenEnhancer(tokenEnhancerChain)

  1. 授权服务器新增TokenEnhancer实现类

    import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
    import org.springframework.security.oauth2.common.OAuth2AccessToken;
    import org.springframework.security.oauth2.provider.OAuth2Authentication;
    import org.springframework.security.oauth2.provider.token.TokenEnhancer;
    import org.springframework.stereotype.Component;
    import java.util.HashMap;
    import java.util.Map;
    
    @Component
    public class CustomTokenEnhancer implements TokenEnhancer {
        @Override
        public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
            Map<String, Object> additionalInfo = new HashMap<>(16);
            additionalInfo.put("userid", "123");
            ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);
            return accessToken;
        }
    }
    
  2. 将tokenEnhancer注入,追加到TokenEnhancerChain中,并设置到endponits

    /**
     * 设置token类型
     *
     * @param endpoints
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
        endpoints.authenticationManager(authenticationManager()).allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST);
        //用户信息
        endpoints.userDetailsService(userDetailsService());
        //自定义token存储,token增强
        //endpoints.tokenStore(tokenStore()).tokenEnhancer(accessTokenConverter());

        TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
        tokenEnhancerChain.setTokenEnhancers(Arrays.asList(tokenEnhancer, accessTokenConverter()));
        endpoints.tokenEnhancer(tokenEnhancerChain);
        endpoints.tokenEnhancer(tokenEnhancer);
    }
  1. 使用密码模式访问
  • http://localhost:8080/oauth/token?grant_type=password&client_id=client_1&client_secret=123456&code=YerSeW&scope=all&username=user_1&password=123456

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/457014.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Vue3+Three.js+antvG2实战项目 智慧城市(四)

前言 在网上找了很久都没有找到使用Three.js开发智慧城市的免费文章或者免费视频,自己花了一点时间做了一个纯前端的智慧城市项目。 技术栈都是最新的:vue3vitetypeScriptThreeantv G2 源码分享 源码 模型,天空图盒子链接分享(不想下载源码可以只用下这个)提取码1234 20230424_…

在时间序列预测中使用 FRFT

RNN Encoder–Decoder 每一个时刻都有一个隐藏状态向量 S_t&#xff0c;当这个向量传递到最后的一个 token 时&#xff0c;即 S_n (n为输入序列的最大长度)&#xff0c;它就是语义编码向量 C C 参与了Decoder过程中每一时刻隐藏状态和输出的计算. 最大的局限性就在于编码和解码…

美国访学J类签证费涨价15%|5月30日生效

2023年5月30日起&#xff0c;美国签证费用开始上调&#xff0c;其中访问学者的J类签证将由160美元调整至185美元&#xff0c;涨价15%。届时美国驻华大使馆将依据汇率做出调整&#xff0c;并公布具体的人民币数额。知识人网小编提醒相关签证人员提前做好准备。 美国国务院发布的…

手撕二叉树(图解+代码)

二叉树 &#x1f333;1.树的概念&#x1f333;2.二叉树的概念及性质&#x1f34e;2.1 二叉树的概念&#x1f34e;2.2 二叉树的性质 &#x1f333;3.二叉树的基本操作&#x1f34e;3.1 二叉树的遍历&#x1f34e;3.2 获取树中节点的个数&#x1f34e;3.3 获取叶子节点的个数&…

Python学习5:计算弓形的面积

类型&#xff1a;数值运算 描述‪‬‪‬‪‬‪‬‪‬‮‬‪‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‪‬‭‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‮‬‪‬‪‬‪‬‪‬‪‬‮‬‭‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‪‬‪‬‪‬‪‬‪‬‪‬‮‬‭‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‪‬ …

大数据学习初级入门教程(十七) —— Elasticsearch 8.7.0 完全分布式集群的安装、配置、启动和测试

好久没用 Elasticsearch 集群了&#xff0c;参考以前写的《大数据学习初级入门教程&#xff08;八&#xff09; —— Elasticsearch 7.6.2 单节点的安装、启动和测试_elasticsearch 7.6.2需要专属网络_孟郎郎的博客-CSDN博客》、《大数据学习初级入门教程&#xff08;九&#x…

面向对象与面向过程的区别

“劳累一天回到家中“ ”对象赶忙问我想吃些什么&#xff1f;“ “望着窗外淅淅沥沥的小雨 蛋炒饭吧” “雨声洗涤了心灵 炒饭温暖了肚子” “我没有问她炒饭是怎么做的&#xff0c;他也没有管我吃相有多难看” “我面向对象&#xff0c;她也面向对象” 面向对象和面向过程的区…

Unity3D:编辑场景模板

场景模板 Inspector 场景模板 Inspector 包含以下部分&#xff1a; Details&#xff1a;指定模板使用哪个场景&#xff0c;并包含模板的描述&#xff0c;该内容将出现在 New Scene 对话框中。Thumbnail&#xff1a;提供用于为模板创建预览图像的选项。Scene Template Pipelin…

ajax的介绍及使用

ajax的介绍 开发流程 前端 ajax:前后端沟通的桥梁 后端 ajax介绍 ajax叫做异步的Javascript和xml ajax通过浏览器与服务器&#xff08;后端&#xff09;进行少量数据交互&#xff0c;进行页面异步更新&#xff08;网页不会重新加载&#xff09; 优点&#xff1a; 减轻服务器负…

Java基础1

一、标识符 给类、接口、方法、变量等取名时用的字符序列 如&#xff1a; public class 类名-标识符{public static void 方法名-标识符(String[] args){int 变量或标识符 1000;} } 二、标识符的命名规范 组成部分 英文大小写字母、数字字符、美元($)符号、下划线(_)、中文…

「ChatGPT」十分钟学会如何在本地调用API_KEY(最新版 | 附源码)

&#x1f482;作者简介&#xff1a; THUNDER王&#xff0c;一名热爱财税和SAP ABAP编程以及热爱分享的博主。目前于江西师范大学会计学专业大二本科在读&#xff0c;同时任汉硕云&#xff08;广东&#xff09;科技有限公司ABAP开发顾问。在学习工作中&#xff0c;我通常使用偏后…

1.2 行列式的性质和计算

学习目标&#xff1a; 当学习行列式性质和计算时&#xff0c;以下是一些具体的学习目标&#xff1a; 理解行列式的定义和计算方法&#xff0c;能够准确计算给定的行列式。&#xff08;最基本的&#xff09;熟练掌握行列式的基本性质&#xff0c;包括交换行列式的两行或两列、…

VRP开源的算例资源

VRP开源的算例资源 开源的算例资源 开源的MIP算例网址 1. MISOCP网址 Benchmark instances&#xff1a;多种问题的算例数据 TSP算例网址 VRP标杆算例网址 1. Networking and Emerging Optimization发布的VRP算例 2. PRP算例 3. 一个学者的主页上的算例 4. Chair in L…

Linux学习笔记(3)一些数据类型

1&#xff09;_u32 是一个无符号的32位整数类型&#xff0c;它在 Linux 内核中定义为 typedef unsigned int __u32。其中&#xff0c;__u32 是为了避免名称冲突而定义的特殊类型。无符号整数是一种表示正整数的数据类型&#xff0c;其取值范围为 0 到 4294967295&#xff08;2…

docker容器:本地私有仓库、harbor私有仓库部署与管理

目录 一、本地私有仓库 1、本地私有仓库简介 2、搭建本地私有仓库 3、容器重启策略介绍 二、harbor私有仓库部署与管理 1、什么是harbor 2、Harbor的特性 3、Harbor的构成 4、harbor部署及配置 ①部署docker-compose ②部署Harbor服务 ③登录创建项目 ④登录仓库并…

对话到行动:通过行动级生成构建面向任务的对话系统

目录 对话到行动:通过行动级生成构建面向任务的对话系统 1介绍 2框架描述 2.1概述 2.2第一步:对话动作构造 2.3步骤2:响应标准化响应标准化 2.4步骤3:动作序列预测 2.5步骤4:生成响应 3实验 3.1实验设置 3.2主要结果 3.3深度分析 4结论 5主持人简介 6公司简介 对…

V-Ray渲染教程:又快又好的V-Ray渲染参数!

Chaos V-Ray 是适用于大部分主流3D设计软件和CAD程序的3D渲染插件&#xff0c;它可以与 3ds Max、Cinema 4D、Houdini、Maya、Nuke、Revit、Rhino、SketchUp、Unreal 无缝协作。借助 V-Ray渲染器强大的功能&#xff0c;艺术家和设计师可以产生出非常逼真的渲染效果。 那么&…

服务提供者 Eureka + 服务消费者(Rest + Ribbon)实战

1、Ribbon背景介绍 Ribbon是Netflix发布的开源项目&#xff0c;主要功能是提供客户端的软件负载均衡算法&#xff0c;将Netflix的中间层服务连接在一起。Ribbon客户端组件提供一系列完善的配置项如连接超时&#xff0c;重试等。简单来说&#xff0c;就是在配置文件中列出Load B…

Word中截取部分内容并保存为jpg图片的方法

private void button1_Click(object sender, EventArgs e) { var appWord new Microsoft.Office.Interop.Word.Application(); var doc new Microsoft.Office.Interop.Word.Document(); object oMissing System.Reflection.Missing.Value;//这个是什么东西&#xff0c;我始终…

等级保护5个级别详细说明-行云管家

目前我国正在严格执行等保政策&#xff0c;但不少企业对于等级保护的5个级别不是很清楚&#xff0c;这里我们行云管家小编就来给大家详细说明一下。 等级保护5个级别详细说明-行云管家 根据《信息安全技术网络安全等级保护基本要求》&#xff0c;安全保护等级分为5个级别&…