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

这篇具有很好参考价值的文章主要介绍了【SpringBoot】自从集成spring-security-oauth2后,实现统一认证授权so easy!。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一.前言

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
    
    【SpringBoot】自从集成spring-security-oauth2后,实现统一认证授权so easy!

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
      

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

3.用户认证分析

4.认证解决方案

4.1.单点登录

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

  • 分布式系统要实现单点登录,通常将认证系统独立抽取出来,并且将用户信息存储在单独数据库中,比如 MySQL、Redis,考虑性能要求,通常存储在Redis中,如下图:
    【SpringBoot】自从集成spring-security-oauth2后,实现统一认证授权so easy!
    Java中有很多用户认证的框架都可以实现单点登录:

    Apache Shiro. 
    CAS 
    Spring security   
    

4.2.第三方账号登录

所谓的第三方登录,是说基于用户在第三方平台上已有的账号和密码来实现应用的登录或者注册的功能。而第三方平台一般是已拥有大量用户的平台,国外的比如Facebook,Twitter等,国内的比如微博、微信、QQ等。
【SpringBoot】自从集成spring-security-oauth2后,实现统一认证授权so easy!

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

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

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

使用QQ认证的过程:
【SpringBoot】自从集成spring-security-oauth2后,实现统一认证授权so easy!

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

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

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


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

二.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
【SpringBoot】自从集成spring-security-oauth2后,实现统一认证授权so easy!

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/然
      【SpringBoot】自从集成spring-security-oauth2后,实现统一认证授权so easy!
  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
    【SpringBoot】自从集成spring-security-oauth2后,实现统一认证授权so easy!

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
【SpringBoot】自从集成spring-security-oauth2后,实现统一认证授权so easy!

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
    【SpringBoot】自从集成spring-security-oauth2后,实现统一认证授权so easy!
  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
      【SpringBoot】自从集成spring-security-oauth2后,实现统一认证授权so easy!
  • 使用token访问资源服务器
    【SpringBoot】自从集成spring-security-oauth2后,实现统一认证授权so easy!

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

1.1.下载oauth2官方建表语句

spring-security-oauth/schema.sql at main · spring-attic/spring-security-oauth · GitHub
【SpringBoot】自从集成spring-security-oauth2后,实现统一认证授权so easy!

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

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

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.测试

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

检查数据库发现测试商户已经导入到数据库了
【SpringBoot】自从集成spring-security-oauth2后,实现统一认证授权so easy!
【SpringBoot】自从集成spring-security-oauth2后,实现统一认证授权so easy!
【SpringBoot】自从集成spring-security-oauth2后,实现统一认证授权so easy!

四.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,登录进入授权页面:
    【SpringBoot】自从集成spring-security-oauth2后,实现统一认证授权so easy!

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

  4. 点击Authorize,确认授权后, 认证服务携带授权码跳转redirect_uri,code=pfiYeD就是返回的授权码, 每一个授权码只能使用一次
    https://www.baidu.com/?code=pfiYeD
    【SpringBoot】自从集成spring-security-oauth2后,实现统一认证授权so easy!

  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
    【SpringBoot】自从集成spring-security-oauth2后,实现统一认证授权so easy!

可能你会遇到返回没有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是我们自己开发的情况下。

测试请求令牌,如下:
【SpringBoot】自从集成spring-security-oauth2后,实现统一认证授权so easy!

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/

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

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

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

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

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

  • 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本身也是安全的,因此这种模式一般用来提供给我们完全信任的服务端使用

测试请求令牌,如下:

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

五.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

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

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

【SpringBoot】自从集成spring-security-oauth2后,实现统一认证授权so easy!文章来源地址https://www.toymoban.com/news/detail-424017.html

到了这里,关于【SpringBoot】自从集成spring-security-oauth2后,实现统一认证授权so easy!的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Spring Security Oauth2.1 最新版 1.1.0 整合 (基于 springboot 3.1.0)gateway 完成授权认证

    目录 背景 demo地址 版本 Spring Boot 3.1 Spring Authorization Server 1.1.0 基础 spring security OAuth2 模块构成 授权方式 认证方式 集成过程 官方demo 代码集成 依赖 授权服务AuthorizationServerConfig配置 重要组件 测试 查看授权服务配置 访问授权服务 授权 回调 获取 access_token 获取用户信息 个性

    2024年02月11日
    浏览(52)
  • SpringBoot2.3集成Spring Security(二) JWT认证

    紧接上文,我们已经完成了 SpringBoot中集成Spring Security,并且用户名帐号和密码都是从数据库中获取。但是这种方式还是不能满足现在的开发需求。 使用JWT的好处: 无状态认证:JWT本身包含了认证信息和声明,服务器不需要在会话中保存任何状态。这样使得应用程序可以更加

    2024年02月11日
    浏览(60)
  • Spring Security OAuth2.0(3):Spring Security简单入门

    Spring Security 快速入门。 本章代码已分享至Gitee:https://gitee.com/lengcz/security-spring-security qquad Spring Secutiry 是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。由于它是Spring生态系统的一员,因此它伴随着整个Spring生态系统不断修正、升级,

    2024年02月13日
    浏览(50)
  • Spring Security OAuth2.0(5):Spring Security工作原理

    qquad Spring Security 所解决的问题就是安全访问控制,而安全访问控制功能其实就是所有进入系统的请求进行拦截,校验每个请求是否能够访问它所期望的资源。Spring Security 对Web资源的保护是通过Filter入手的,所以从这个Filter入手,逐步深入Spring Security原理。 $qquad%当初始化

    2024年02月17日
    浏览(56)
  • Spring Security OAuth 2.0

    1 概念 OAuth 2.0 到底是什么呢?我们先从字面上来分析下。OAuth 2.0 一词中的字母 “O” 是 Open 的简称,表示 “开放” , “Auth” 表示 “授权”,连在一起就表示 “开放授权”。 OAuth 2.0是一种授权框架,提供了一套规范和协议,用于实现授权流程和访问令牌的管理,而非单个

    2024年02月16日
    浏览(50)
  • Spring Security OAuth2详解

    spring security oauth2框架即spring security + OAuth2,spring security上一篇文章已经讲过,接下来讲讲OAuth2,它是行业标准的授权协议,旨在为开发人员提供简单易用的授权流程;OAuth 定义了四种角色: 资源所有者:能够授予对受保护资源的访问权限的实体,当资源所有者是一个人时,

    2024年02月03日
    浏览(41)
  • Spring Security OAuth2.0 - 学习笔记

    OAuth2.0是一个开放标准,允许用户授权第三方应用程序访问他们存储在另外的服务提供者上的信息,而不需要将用户和密码提供给第三方应用或分享数据的所有内容。 1)授权码模式 2)简化模式 3)密码模式 4)客户端模式 普通令牌只是一个随机的字符串,没有特殊的意义,

    2024年02月16日
    浏览(50)
  • Spring Security OAuth2 远程命令执行漏洞

    cd vulhub/spring/CVE-2016-4977/ docker-compose up -d 访问 http://192.168.10.171:8080/oauth/authorize?response_type=${233*233}client_id=acmescope=openidredirect_uri=http://test 用admin:admin登陆 出现以下报错,表示漏洞存在(response_type里面的命令执行了) poc.py #!/usr/bin/env python message = input(‘Enter message to encode:’) p

    2024年02月09日
    浏览(42)
  • 微服务安全Spring Security Oauth2实战

    Spring Authorization Server 是一个框架,它提供了 OAuth 2.1 和 OpenID Connect 1.0 规范以及其他相关规范的实现。它建立在 Spring Security 之上,为构建 OpenID Connect 1.0 身份提供者和 OAuth2 授权服务器产品提供了一个安全、轻量级和可定制的基础。说白了,Spring Authorization Server 就是一个**认

    2024年02月03日
    浏览(39)
  • Spring Security与OAuth2的完美结合

    OAuth2是一种流行的授权框架,它允许用户授权第三方应用程序访问他们的资源。Spring Security是一个强大的安全框架,它提供了一系列的安全功能。本文将介绍如何将Spring Security与OAuth2整合,以实现更加安全和可靠的应用程序。 OAuth2的基本概念 OAuth2是一个授权框架,它允许用

    2024年02月05日
    浏览(45)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包