OAuth2单点登录实现

前言技术之Oauth2全方面介绍 一、Oauth2基本概念 1、定义 Oauth2是目前最流行的授权机制,用来授权第三方应用,获取用户数据 2、场景带入 1、外卖场景 一户人家住在一个大型居住小区,小区有门禁系统,进入小区需要输入密码,这户人家经常性点外卖,必须让外卖人员进入到小区,如果把密码告诉外卖人员,这样的话,他就和户主拥有了一样的权限,这样安全隐患太大,给了外面人员密码之后,为了安全,必须要进行修改密码,这就很麻烦。 这时候Oauth2就诞生了,外卖人员的职责只是送货,没必要知道小区密码 ... 阅读详情
OAuth2客户端注解

一、介绍

1、相关概念

首先,要说明使用之前,我们来了解一下oauth2提供的几个注解相关功能:

  • @EnableOAuth2Client
    客户端,提供OAuth2RestTemplate,用于客户端访问资源服务。
    简要步骤:客户端访问资源->客户端发现没有资源访问token->客户端根据授权类型生成跳转url->浏览器 302 到认证授权服务进行认证、授权。
  • @EnableOAuth2Sso
    应用系统,使用远端认证授权服务,替换应用自身的用户登录鉴权security逻辑,实现单点登录功能。
    简要步骤:访问应用系统资源-> 应用系统发现未登录-> 302 跳转到登录页面(登录页面地址已经与获取token逻辑自动关联)-> 应用系统发现符合获取token条件,根据授权类型拼装url->302 跳转到认证授权地址(认证授权服务提供)进行认证、授权。
  • @EnableAuthorizationServer
    认证授权服务,提供用于获取token,解析token相关功能,实现认证、授权功能。
    具体见 Spring Security 文章目录中的 Spring Cloud OAuth2 五种授权方式介绍。
  • @EnableResourceServer
    资源服务,提供基于token的资源访问功能。

所谓的单点登录(Single Sign On,简称SSO)是指许多个逻辑上相互独立的系统或软件构成一个完整的系统的平台中仅有唯一的授权系统,多个系统或软件使用授权系统进行统一认证和授权,对浏览器或客户端来说就是都是使用同一个授权系统进行登录授权,用户只需要登录一次就可以访问所有相互信任的应用系统。

根据以上概念,我们只需要专注两点:

  • 只有一个授权系统(这里的一个是一类集群,可包含多个服务,如微服务中的多个授权服务)
  • 用户不论开始访问哪个系统,只需要登录一次就可以到处访问

2、系统流程

理解SSO单点登录概念之后,我们就需要知道SSO是如何实现多个系统统一认证授权的,这里我找了一张URL时序图,如下所示:
在这里插入图片描述

这张图画的非常清晰,已经对整理流程描述的非常到位,这里我稍作解析,流程解析如下:

  • 首先,浏览器访问系统1,由于没有登录则跳转到授权系统
  • 授权系统发现该session用户并没有登录则返回授权系统的登录页面(可以自定义)
  • 用户通过统一的登录页面输入用户名密码进行登录
  • 授权系统进行认证(Provider)并创建令牌返回系统1
  • 系统1通过设置的userAuthorizationUri到授权服务进行授权并携带自己的重定向url
  • 授权服务校验令牌有效并返回系统1
  • 系统1通过用户认证信息坚定是否具有该资源的访问权限,有则通过无则拒绝

当用户登录后再次访问系统2的流程如下:

  • 用户访问系统2,系统2发现未登录跳转到授权系统
  • 授权系统用户会话查找发现用户已登录,跳转到系统2并携带令牌
  • 系统2到授权服务校验令牌有效性,验证成功后跳转到系统2
  • 系统2验证用户权限,通过2允许访问受保护资源

二、实战实例

为了能说明整个系统的流程,我们必须要准备至少3个系统,它包括:

  • 授权系统
    进行所有服务的统一认证授权。
  • 资源系统A
    提供A资源的访问。
  • 资源系统B
    提供B资源的访问。

正如我的《oauth2资源服务》一文中看到的,资源服务要到认证服务获取用户认证信息最终是通过OAuth2RestOperations对象请求配置的tokenInfoUri或userInfoUri从授权服务获取的。对于单点登录配置原理也是一样,它也是通过一个对象OAuth2RestOperations的实现类实现OAuth2RestTemplate的,只不过,我们看不到里面的实现而已,至于里面的加载过程,我找到一张图,如下图所示:

至于代码的加载流程与《oauth2在这里插入图片描述
资源服务》中的一致,感兴趣的可以根据我的思路一步一步端点跟踪,这里我就不再贴出代码来讲解加载过程中做了什么。

1、系统配置

(1)授权系统配置
首先,我们要准备基础的用户数据,用于用户登录,这里主要是注册客户端数据,包括客户端id、秘钥、授权模式、重定向地址列表、过期时间等,我的测试数据如下所示:

my_client_id	
gate_way_server	
$2a$10$9mmTWJd1pJ2OjWKG1G1pNuyUxIG6Lv8lic42VmBXYrVNG4ZB9FwL6	
user_info	
authorization_code,refresh_token,implicit,password,client_credentials	
http://www.baidu.com,http://localhost:7001/demon/login,http://localhost:7001/A/login,http://localhost:7002/B/login	
ROLE_ADMIN	
7200	
86400	
{"systemInfo":"Atlas System"}	
true

在这里插入图片描述

这里要注意redirect_url参数,它决定了授权码code回传到重定向的地址,对于子系统A和B来说,他们的重定向地址是http://xxxx/服务名/login; 所以这里填写了两个系统的地址,注意不要写错否则,报错:

Invalid redirect: http://localhost:7001/A/login does not match one of the registered values.

在这里插入图片描述

(2) 客户端服务
关于客户端,是我们提供给外部浏览器访问的前置服务,最终所有的权限验证我们通过SSO登录到授权服务进行验证。所以,我这里的两个子系统配置必须配置资源服务以及授权client相关信息(注意,我用的不是jwt加密模式),配置文件如下所示:

系统A配置:

#oauth2客户端
security:  
  oauth2:
    resource:
      filter-order: 3
      id: resource_server_id
      tokenInfoUri: http://127.0.0.1:7000/oauth/check_token
      preferTokenInfo: true
      #user-info-uri: http://127.0.0.1:7000/user/principal
      #prefer-token-info: false
    #如下可暂时不用配置-仅做保留
    client:
      accessTokenUri: http://127.0.0.1:7000/oauth/token
      userAuthorizationUri: http://127.0.0.1:7000/oauth/authorize
      clientId: my_client_id
      clientSecret: my_client_secret

系统B配置:

#oauth2客户端
security:  
  oauth2:
    resource:
      filter-order: 3
      id: resource_server_id
      tokenInfoUri: http://127.0.0.1:7000/oauth/check_token
      preferTokenInfo: true
      #user-info-uri: http://127.0.0.1:7000/user/principal
      #prefer-token-info: false
    client:
      accessTokenUri: http://127.0.0.1:7000/oauth/token
      userAuthorizationUri: http://127.0.0.1:7000/oauth/authorize
      clientId: my_client_id
      clientSecret: my_client_secret

这里我说明一下对应参数,resource是资源服务相关参数,client是客户端先关参数,对应的地址都是授权服务提供的接口地址,各个接口含义通过《oauth2认证流程》和《oauth2资源服务》两篇文章大家都应该了解到。

初次之外,我们在客户端需要添加SSO相关包,引入SSO相关配置及类,所以以子系统的pom配置如下所示:

<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.easystudy</groupId>
	<artifactId>SSOClient</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>pom</packaging>
	<name>SSO单点登录系统</name>
	<modules>
		<module>SystemA</module>
		<module>SystemB</module>
	</modules>
  
     	<!-- spring boot项目 -->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.4.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <!-- 项目依赖管理声明,统一管理项目依赖的版本信息,继承项目无需声明版本 -->
    <dependencyManagement>
        <!-- 依赖管理  -->
        <dependencies>
            <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-dependencies -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Greenwich.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-dependencies -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-security-dependencies</artifactId>
                <version>2.1.0.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <!-- 项目属性:子模块不能引用父项目的properties变量 -->
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <spring-cloud.version>Finchley.RELEASE</spring-cloud.version>
        <!-- <spring-cloud.version>Finchley.BUILD-SNAPSHOT</spring-cloud.version>-->
        <lombok.version>1.16.20</lombok.version>
        <spring-security-oauth2-autoconfigure.version>2.1.0.RELEASE</spring-security-oauth2-autoconfigure.version>
    </properties>
  
	<!-- 项目依赖:特殊强制依赖,其他继承父亲 -->
	<dependencies>
		<!-- 监控系统健康情况的工具 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-actuator</artifactId>
		</dependency>
		<!--Lombok:消除模板代码-->
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
		</dependency>
		<!-- logback日志包 -->
		<dependency>
		    <groupId>ch.qos.logback</groupId>
		    <artifactId>logback-core</artifactId>
		</dependency>
		<!-- SpringMVC -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<!-- 资源服务器 -->
		<dependency>
		    <groupId>org.springframework.cloud</groupId>
		    <artifactId>spring-cloud-starter-security</artifactId>
		</dependency>
		<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-oauth2-client</artifactId>
        </dependency>
		<dependency>
		    <groupId>org.springframework.security.oauth.boot</groupId>
		    <artifactId>spring-security-oauth2-autoconfigure</artifactId>
		    <version>2.1.4.RELEASE</version><!--$NO-MVN-MAN-VER$-->
		</dependency>
		<!--
		<dependency>
		    <groupId>org.springframework.cloud</groupId>
		    <artifactId>spring-cloud-starter-oauth2</artifactId>
		    <version>1.2.2.RELEASE</version>
		</dependency> 
		 -->
		 <!-- RESTFul接口文档:通过接口访问文档:http://localhost:8762/swagger-ui.html,8762为服务配置的监听端口,默认8080 -->
		<dependency>
			<groupId>io.springfox</groupId>
			<artifactId>springfox-swagger2</artifactId>
			<version>2.8.0</version>
		</dependency>
		<dependency>
			<groupId>io.springfox</groupId>
			<artifactId>springfox-swagger-ui</artifactId>
			<version>2.8.0</version>
		</dependency>
		<!-- 配置文件读取 -->
		<dependency>
		   	<groupId>org.springframework.boot</groupId>
		   	<artifactId>spring-boot-configuration-processor</artifactId>
		   	<optional>true</optional>
		</dependency>
	</dependencies>
	<!-- 编译插件 -->
	<build>
	    <plugins>
	    	  <!--spring boot maven插件-->
	        <plugin>
	            <groupId>org.springframework.boot</groupId>
	            <artifactId>spring-boot-maven-plugin</artifactId>
	        </plugin>
	    </plugins>
	</build>
	
</project>

2、代码实现

有了以上配置之后,我们就需要实现授权系统和客户端系统,我们首先来看客户端系统的安全配置:

package com.easystudy.config;

import org.springframework.boot.autoconfigure.security.oauth2.client.EnableOAuth2Sso;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;

/**
 * @文件名称: WebSecurityConfig.java
 * @功能描述: WebSecurity安全配置
 * @版权信息: www.easystudy.com
 * @技术交流: 961179337(QQ群)
 * @编写作者: lixx2048@163.com
 * @联系方式: 941415509(QQ)
 * @开发日期: 2020年7月26日
 * @备注信息: 任何需要身份验证的请求都将被重定向到授权服务器
 */
@Configuration
@EnableOAuth2Sso
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    
    /**
     * @功能描述: 受保护资源访问策略配置
     * @编写作者: lixx2048@163.com
     * @开发日期: 2020年7月26日
     * @历史版本: V1.0  
     * @参数说明:
     * @返  回  值:
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {       
    	// 资源访问安全策略
    	ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = http.authorizeRequests();
		registry
			.anyRequest()
				.authenticated()
	    	// 跨域请求配置
	    	.and()
	    		.csrf().disable();        
    }
    
    /**
     * @功能描述: 静态资源忽略放行配置
     * @编写作者: lixx2048@163.com
     * @开发日期: 2020年7月26日
     * @历史版本: V1.0  
     * @参数说明:
     * @返  回  值:
     */
    @Override
    public void configure(WebSecurity web) throws Exception {
    	// 放行静态资源,否则添加oauth2情况下无法显示
        web.ignoring().antMatchers("/favor.ico", "/favicon.ico","/v2/api-docs", "/swagger-resources/configuration/ui",
                "/swagger-resources","/swagger-resources/configuration/security",
                "/swagger-ui.html","/css/**", "/js/**","/images/**", "/webjars/**", "**/favicon.ico", "/index");
    }
}

我们做了两项配置,一个是资源访问权限配置,这里配置为authenticated,也就是所有资源都必须认证后才能访问。一个是静态资源访问,针对所有静态资源都忽略权限(放行);

接下来,就是授权服务器配置:

package com.easystudy.config;

import java.util.concurrent.TimeUnit;

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.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.ClientDetailsService;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.store.JdbcTokenStore;

import com.easystudy.oauth.UsernameUserDetailService;

/**
 * @文件名称: AuthorizationServerConfig.java
 * @功能描述: 授权服务配置类,配置token存储方式、clientDetail等
 * @版权信息: www.easystudy.com
 * @技术交流: 961179337(QQ群)
 * @编写作者: lixx2048@163.com
 * @个人信息:941415509
 * @开发日期: 2020年7月27日
 * @历史版本: V1.0
 */
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private DataSource dataSource;
    @Autowired
    private UsernameUserDetailService userDetailsService;
    
    @Primary
    @Bean
    public JdbcTokenStore jdbcTokenStore(){
        return new JdbcTokenStore(dataSource);
    }  
    
    @Bean
    public ClientDetailsService clientDetails() {
    	return new JdbcClientDetailsService(dataSource);
    }
    
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.withClientDetails(clientDetails());
    }
    
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints
        		 // 使用的认证管理器-默认包含登录认证、用户名密码认证
        		 .authenticationManager(authenticationManager)
        		 // token存储在数据库中-生产环境使用以免服务器崩溃
        		 .tokenStore(jdbcTokenStore())
        		 // 设置refresh token是否重复使用,若无,refresh_token会有UserDetailsService is required错误
        		 .reuseRefreshTokens(false)
        		 // 从数据查用户授权信息
                 .userDetailsService(userDetailsService)
                 // 授权请求提交方式
                 .allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST);
        endpoints.tokenServices(defaultTokenServices());
    }

    @Primary
    @Bean
    public DefaultTokenServices defaultTokenServices(){
        DefaultTokenServices tokenServices = new DefaultTokenServices();
        tokenServices.setTokenStore(jdbcTokenStore());										// token存储在redis中-如果存储在jdbc中就需要建立token存储表
        tokenServices.setSupportRefreshToken(true);											// 支持更换token
        tokenServices.setClientDetailsService(clientDetails());								// jdbc具体的秘钥认证服务-如果存储在jdbc中就需要建立oauth_client_details表
        tokenServices.setAccessTokenValiditySeconds((int) TimeUnit.HOURS.toSeconds(2)); 	// token有效期自定义设置,2小时
        tokenServices.setRefreshTokenValiditySeconds((int) TimeUnit.DAYS.toSeconds(1));		// 1天之内可以刷新token
        return tokenServices;
    }

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security.tokenKeyAccess("permitAll()");
        security.checkTokenAccess("isAuthenticated()");
        security.allowFormAuthenticationForClients();
    }
}

这里我使用的jdbc存储token方式,授权服务的安全配置如下:

package com.easystudy.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
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.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;

import com.easystudy.oauth.UsernameUserDetailService;

/**
 * @文件名称: WebSecurityConfig.java
 * @功能描述: WebSecurity安全配置
 * @版权信息: www.easystudy.com
 * @技术交流: 961179337(QQ群)
 * @编写作者: lixx2048@163.com
 * @联系方式: 941415509(QQ)
 * @开发日期: 2020年7月26日
 * @历史版本: V1.0
 */
@Configuration
@Order(2)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private UsernameUserDetailService usernameUserDetailService;
      
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    /**
     * @功能描述: 自定义认证管理器配置
     * @编写作者: lixx2048@163.com
     * @开发日期: 2020年7月26日
     * @历史版本: V1.0  
     * @参数说明:
     * @返  回  值:
     */
    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
    
    /**
     * @功能描述: 受保护资源访问策略配置
     * @编写作者: lixx2048@163.com
     * @开发日期: 2020年7月26日
     * @历史版本: V1.0  
     * @参数说明:
     * @返  回  值:
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {       
    	// 资源访问安全策略
    	ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = http.authorizeRequests();
		registry
			// 任何配置都需要登录认证
			//.antMatchers("/oauth/user/me")
			//	.permitAll()
			.anyRequest()
				.authenticated()
			// 登录地址配置以及登录成功默认主页配置
			.and()
		    	.formLogin()
		    		.loginPage("/login")
		    		.defaultSuccessUrl("/home")
		    		.permitAll()
		    // 登出接口权限配置
	    	.and()
	    		.logout().permitAll()
	    	// 跨域请求配置
	    	.and()
	    		.csrf().disable()
	    	// 进行http Basic认证
	    	.httpBasic();        
    }
    
    /**
     * @功能描述: 认证管理提供者列表配置
     * @编写作者: lixx2048@163.com
     * @开发日期: 2020年7月26日
     * @历史版本: V1.0  
     * @参数说明:
     * @返  回  值:
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(daoAuthenticationProvider());
    }
    
    /**
     * @功能描述: 数据库认证提供者配置
     * @编写作者: lixx2048@163.com
     * @开发日期: 2020年7月26日
     * @历史版本: V1.0  
     * @参数说明:
     * @返  回  值:
     */
    @Bean
    public DaoAuthenticationProvider daoAuthenticationProvider(){
        DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
        // 设置userDetailsService
        provider.setUserDetailsService(usernameUserDetailService);
        // 禁止隐藏用户未找到异常
        provider.setHideUserNotFoundExceptions(false);
        // 使用BCrypt进行密码的hash
        provider.setPasswordEncoder(passwordEncoder());
        return provider;
    }
    
    /**
     * @功能描述: 认证失败地址配置
     * @编写作者: lixx2048@163.com
     * @开发日期: 2020年7月26日
     * @历史版本: V1.0  
     * @参数说明:
     * @返  回  值:
     */
    @Bean
    public AuthenticationFailureHandler simpleUrlAuthenticationFailureHandler() {
        return new SimpleUrlAuthenticationFailureHandler("/login?error");
    }
    
    /**
     * @功能描述: 静态资源忽略放行配置
     * @编写作者: lixx2048@163.com
     * @开发日期: 2020年7月26日
     * @历史版本: V1.0  
     * @参数说明:
     * @返  回  值:
     */
    @Override
    public void configure(WebSecurity web) throws Exception {
    	// 放行静态资源,否则添加oauth2情况下无法显示
        web.ignoring().antMatchers("/favor.ico", "/favicon.ico","/v2/api-docs", "/swagger-resources/configuration/ui",
                "/swagger-resources","/swagger-resources/configuration/security",
                "/swagger-ui.html","/css/**", "/js/**","/images/**", "/webjars/**", "**/favicon.ico", "/index");
    }
}

此外,A系统提供受保护的资源接口,实现如下:

package com.easystudy.controller;

import java.security.Principal;

import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;

/**@文件名称: TestController.java
 * @功能描述: TODO(用一句话描述该文件做什么)
 * @版权信息: www.easystudy.com
 * @技术交流: 961179337(QQ群)
 * @编写作者: lixx2048@163.com
 * @联系方式: 941415509(QQ)
 * @开发日期: 2020年7月27日
 * @历史版本: V1.0  
 */
@RestController
@RequestMapping("/test")
@Api(value = "OAuth2 Client测试接口文档", tags = "OAuth2 Client测试接口文档")
public class TestController {
	
	// oauth2注解
	/**
	 * @RequiresUser:subject.isRemembered()结果为true,subject.isAuthenticated() 
	 * @RequiresAuthentication:同于方法subject.isAuthenticated() 结果为true时
	 * @RequiresGuest:与@RequiresUser完全相反。
	 * @RequiresRoles("xx");有xx角色才可以访问方法
	 * @RequiresPermissions({"file:read", "write:aFile.txt"} ):同时含有file:read和write:aFile.txt的权限才能执行方法
	 */
	@GetMapping("/hi")
	@ApiOperation(value="打招呼1", notes="打招呼1")
	@ApiImplicitParams({ @ApiImplicitParam(paramType = "query", dataType = "String", name = "name", value = "名称", required = true) })
	public String hi(@RequestParam(name = "name", required = true) String name){
		return "A系统:hi," + name;
	}
	
	/**
	 * @功能描述: 获取用户认证信息(已登录用户)
	 * @技术交流: 961179337(QQ群)
	 * @编写作者: lixx2048@163.com
	 * @联系方式: 941415509(QQ)
	 * @开发日期: 2020年7月31日
	 */
	@GetMapping("/info")
    public Principal info(Principal principal) {
        return principal;
    }
	
	@GetMapping("/me")
	//@PreAuthorize("hasAuthority('member:save')")
    public Authentication me(Authentication authentication) {
        return authentication;
    }
}

B系统提供受保护的资源接口,实现如下:

package com.easystudy.controller;

import java.security.Principal;

import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;

/**@文件名称: TestController.java
 * @功能描述: TODO(用一句话描述该文件做什么)
 * @版权信息: www.easystudy.com
 * @技术交流: 961179337(QQ群)
 * @编写作者: lixx2048@163.com
 * @联系方式: 941415509(QQ)
 * @开发日期: 2020年7月27日
 * @历史版本: V1.0  
 */
@RestController
@RequestMapping("/test")
@Api(value = "OAuth2 Client测试接口文档", tags = "OAuth2 Client测试接口文档")
public class TestController {
	
	// oauth2注解
	/**
	 * @RequiresUser:subject.isRemembered()结果为true,subject.isAuthenticated() 
	 * @RequiresAuthentication:同于方法subject.isAuthenticated() 结果为true时
	 * @RequiresGuest:与@RequiresUser完全相反。
	 * @RequiresRoles("xx");有xx角色才可以访问方法
	 * @RequiresPermissions({"file:read", "write:aFile.txt"} ):同时含有file:read和write:aFile.txt的权限才能执行方法
	 */
	@GetMapping("/hi")
	@ApiOperation(value="打招呼1", notes="打招呼1")
	@ApiImplicitParams({ @ApiImplicitParam(paramType = "query", dataType = "String", name = "name", value = "名称", required = true) })
	public String hi(@RequestParam(name = "name", required = true) String name){
		return "B系统:hi," + name;
	}
	
	/**
	 * @功能描述: 获取用户认证信息(已登录用户)
	 * @技术交流: 961179337(QQ群)
	 * @编写作者: lixx2048@163.com
	 * @联系方式: 941415509(QQ)
	 * @开发日期: 2020年7月31日
	 */
	@GetMapping("/info")
    public Principal info(Principal principal) {
        return principal;
    }
	
	@GetMapping("/me")
	//@PreAuthorize("hasAuthority('member:save')")
    public Authentication me(Authentication authentication) {
        return authentication;
    }
}

3、接口测试

测试目标和结果1:
1、浏览器访问受保护的系统A的接口:/test/hi?name=lixx会重定向到授权服务的登录页面;
2、用户输入用户名密码点击确认后,授权服务重定向到原来的地址/test/hi?name=lixx
3、系统A返回打印结果:“A系统:hi,lixx”

测试目标和结果2:
1、浏览器访问受保护的系统B的接口:/test/hi?name=lixx
2、系统B直接从授权服务返回对应token并自动验证通过并重定向到/test/hi?name=lixx,用户不在需要输入用户名密码;
3、系统B返回打印结果:“A系统:hi,lixx”

我们按照我们的测试目标和结果一步步测试:

浏览器访问A系统的接口

访问地址:

http://localhost:7001/A/test/hi?name=lixx 

由于用户未登录,重定向授权服务器的登录页面:

http://localhost:7000/login  

在这里插入图片描述

用户登录授权跳转系统A接口

在这里插入图片描述

浏览器访问B系统的接口

访问地址:

http://localhost:7002/B/test/hi?name=lixx

系统B直接返回接口数据(无需登录)

在这里插入图片描述

三、相关总结

单点登录实现原理同资源服务与授权服务差不多,只不过内部获取token的过程不太一样:

1、内部实现原理
单点登录使用的授权码模式重定向设定的地址,然后在通过code换取token;而资源服务是通过从授权服务获取自定义接口提供用户认证信息或通过token获取认证信息;

2、交互方式
单点登录针对的是浏览器客户端,有一个重定用户授权过程(授权码模式),需要用户输入用户名和密码进行干预;
资源服务则有两种方式:

  • 从授权服务获取token,然后浏览器携带token访问资源服务,资源服务通过token获取用户认证信息然后进行权限校验;
  • 将用户名密码(这里不是客户id和秘钥)编码后作为access_token访问资源服务,资源服务通过Authorization头获取用户信息访问授权服务获取用户认证信息然后进行权限校验;

3、使用场景
单点登录一般用户前端资源服务; 资源服务一般用于后台的接口服务;

以上是本人的自我总结,不对之处,欢迎各位提出宝贵意见!

源码获取、合作、技术交流请获取如下联系方式:

QQ交流群:961179337
在这里插入图片描述

微信账号:lixiang6153
公众号:IT技术快餐
电子邮箱:lixx2048@163.com

【OAuth2】OAuth2概述及使用GitHub登录第三方网站 OAuth 是一个开放的非常重要的认证标准/协议,允许用户授权第三方应用访问其存储在其他网站上资源,而无需将用户名密码提供给第三方网站的开放标准/协议。OAuth2 是 OAuth 的最新版本,同时也是被广泛应用的一个版本。我们在网站上常见的QQ登录,微信扫码登录,GitHub 授权登录就是基于 OAuth2.0 实现的。点击跳转。 阅读详情

相关推荐

OAuth2.0实现单点登录

在一开始的时候,应用服务器(客户端通过访问自己的应用服务器来进而访问其他服务)和验证服务器之间会共享一个 secret,这个东西没有其他人知道,而验证服务器在用户验证完成之后,会返回一个授权码,应用服务器最后将授权码和 secret 一起交给验证服务器进行验证,并且 Token 也是在服务端之间传递,不会直接给到客户端。首先用户访问页面时,会重定向到认证服务器,接着认证服务器给用户一个认证页面,等待用户授权,用户填写信息完成授权后,认证服务器返回 Token。

GSON的博客 5985

【OAuth2】详细讲解

Oauth2的基本概念与四种认证模式的详细讲解 ​​​​​​​

Huang_Ds的博客 2万+

OAuth2实现单点登录SSO

1.前言 技术这东西吧,看别人写的好像很简单似的,到自己去写的时候就各种问题,“一看就会,一做就错”。网上关于实现SSO的文章一大堆,但是当你真的照着写的时候就会发现根本不是那么回事儿,简直让人抓狂,尤其是对于我这样的菜鸟。几经曲折,终于搞定了,决定记录下来,以便后续查看。先来看一下效果 2.准备 2.1...

javarrr的博客 1万+

OAuth2 详细介绍!

OAuth2 , Spring Security OAuth2 , JWT

weixin_52834606的博客 1万+

细说STM32单片机SPI-Flash芯片的FatFS移植及其应用实例详解

本文以开发板上的SPI-Flash芯片W25Q16为媒介详细介绍CubeMX中FatFS各个参数的意义和设置,分析生成的代码中FatFS各个文件的作用和关联,完成针对W25Q16的硬件访问层的移植。继续使用旺宝红龙开发板STM32F407ZGT6 KITV1.0。

wenchm的博客 1640

Maven的下载安装配置教程(详细图文)

目录 一、简单了解一下什么是Maven 二、maven的下载 三、maven的安装 四、maven的环境变量配置 五、setting文件配置 六、开发工具配置Maven 一、简单了解一下什么是Maven Maven就是一款帮助程序员构建项目的工具,我们只需要告诉Maven需要哪些Jar 包,它会帮助我们下载所有的Jar,极大提升开发效率。 1.Maven翻译为“专家“, ”内行”的意思,是著名Apache公司下基于Java开发的开源项目。 2.Maven项目对象模型(POM)是一..

bakelFF的博客 12万+

OAuth2.0 实现单点登录

本文是对OAuth2.0的学习,了解了它的4种授权方式,学会了如何搭建验证服务器,使用 postman对四种模式作了接口测试。并且学会了资源服务器实现单点登录的两种方式,也成功解决了远程调用时没有携带 token 的问题。最后也学会了使用 JWT 存储 Token,大大提高了安全性。

qq15035899256的博客 4万+

华硕主板UEFI模式下安装Ubuntu与Windows双系统全攻略

本文详细讲解了在华硕主板UEFI模式下安装Ubuntu与Windows双系统的完整流程。从安装前的BIOS关键设置(如处理安全启动与CSM)、使用GPT分区表,到在Windows中安全分配空间、手动创建Ubuntu分区,最后完成引导配置,提供了避免常见错误的实用技巧,确保双系统稳定共存。

weixin_29199873的博客 346

OAuth2实现单点登录

OAuth2实现单点登录 统一认证中心 一、添加依赖略~看前几章! 二、项目的启动类上添加 @EnableResourceServer 注解,表示这是一个资源服务器 接下来我们进行授权服务器的配置,由于资源服务器和授权服务器合并在一起,因此授权服务器的配置要省事很多: @Configuration @EnableAuthorizationServer public class AuthServerConfig extends AuthorizationServerConfigurerAdapter {

Curtisjia的博客 1061

转录组分析中的Fastp和FastQC:如何快速检测数据质量并优化流程

本文深入探讨了转录组分析中数据质量控制的核心流程,重点介绍了Fastp和FastQC两款工具的组合应用。FastQC负责对原始测序数据进行全面质量评估,生成可视化报告;Fastp则能一站式完成接头切除、低质量序列过滤、长度筛选等关键质控步骤。文章提供了详细的参数解读、实战脚本和自动化流程构建指南,帮助研究人员快速检测数据质量并优化预处理流程,为下游的差异表达等分析奠定可靠基础。

weixin_28452161的博客 182

深入浅出:UE4类型系统完整构建过程详解

UE4的类型系统是一个复杂但设计精巧的架构,它通过静态代码生成和运行时构建的完美配合,实现了强大的反射功能。理解这个过程不仅有助于解决开发中的问题,也能让你更好地利用UE4的各种高级特性。就像建造一栋大楼需要经过设计、备料、框架、装修、验收等多个阶段,UE4类型系统的构建也是一个多阶段、精细化的过程。每个阶段都有其特定的职责,共同协作完成从源代码到运行时对象的转换。

m0_45371381的博客 911

使用Spring Security OAuth2实现单点登录(SSO)系统

一、单点登录SSO介绍   目前每家企业或者平台都存在不止一套系统,由于历史原因每套系统采购于不同厂商,所以系统间都是相互独立的,都有自己的用户鉴权认证体系,当用户进行登录系统时,不得不记住每套系统的用户名密码,同时,管理员也需要为同一个用户设置多套系统登录账号,这对系统的使用者来说显然是不方便的。我们期望的是如果存在多个系统,只需要登录一次就可以访问多个系统,只需要在其中一个系统执行注销登录操作,则所有的系统都注销登录,无需重复操作,这就是单点登录(Single Sign On 简称SSO)系统实现的功

一瓶绿茶三元钱 1万+

2021年03月 C/C++(五级)真题解析#中国电子学会#全国青少年软件编程等级考试

给定一个十进制正整数n(0 < n < 1000000000),每个数位上数字均不为0。n的位数为m。现在从m位中删除k位(0

人生就是一个不断学习的过程 1011

使用OAuth2实现单点登录

要想自己定制,需要把OAuth2协议和SSO思想策略弄通才行。我使用OAuth2的密码模式实现了简单的SSO,我使用oauthserver作为授权服务器,使用oauthresource作为客户端。可以根据情况使用@EnableOAuth2Sso注解使用授权码模式(更安全)登录。oauthserver上实现继承了AuthorizationServerConfigurerAdapter和WebSecurityConfigurerAdapter的类;

make_progress的博客 2565

00 — Intel xe SVM 内核实现精读系列专栏导读

本文系统介绍了Intel Xe GPU的共享虚拟内存(SVM)实现机制。第一部分通过概念地图阐述SVM的六个核心概念及其关系;第二部分通过三个测试用例解析SVM关键功能:fault生命周期验证、madvise与VRAM迁移、跨GPU一致性;第三部分深入内核实现,包括初始化/销毁流程、Page Fault处理、MMU Notifier失效路径、异步垃圾回收、VRAM迁移机制、madvise实现原理,以及与ROCm SVM的对比。文章建议按功能模块顺序学习,从基本概念到测试用例,再深入内核实现细节,最后对比不同

图形/计算GPU的驱动与虚拟化技术分享园地 1671

uniapp [全端兼容] - 实现全景图Vr 720°全景效果查看预览功能,3D全景图流畅不卡顿渲染+手势拖拽+悬浮工具按钮,uniAPP实现vr看720度全景效果示例代码(H5小程序APP全兼容)

uniapp显示720度全景图,720°全景图显示,在h5中展示720°全景图,浏览全景图,基于uniApp实现vr全景预览,加载全景图,uni-app全景预览插件,UNI-APP实现3D全景图,实现720°全景图展示,uniapp全景视频播放,uniapp项目实现vr720度全景图片预览,在网页上展示760全景图片和视频,全景图场景切换,UNIAPP全景VR图,uniapp加载720全景图_uniapp怎么加载vr图片,uniapp实现vr全景看房,uni-app实现全景效果,加载很慢卡怎么办,实现全景预

🏆 前端领域优质创作者 1万+

Oauth2单点登录 vue前后端分离实现

Oauth2+Vue实现前后端分离,多域名下的单点登录。

weixin_39515823的博客 5608

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

Spring Cloud Security:Oauth2实现单点登录

Spring Cloud Security 为构建安全的SpringBoot应用提供了一系列解决方案,结合Oauth2可以实现单点登录功能,本文将对其单点登录用法进行详细介绍。单点登录(Single Sign On)指的是当有多个系统需要登录时,用户只需登录一个系统,就可以访问其他需要登录的系统而无需登录。

weixin_42907150的博客 1308

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

Oauth2实现单点登录

Oauth2单点登录具体实现

2301_81479055的博客 746

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

应用系统基于OAuth2实现单点登录的解决方案

基于OAuth2的认证方式包含四种,其中单点登录最常用的是授权码模式。应用系统与oauth2集成实现单点登录,关键的步骤包括:统一用户数据源、集成oahtu2认证服务,改造应用系统登录逻辑和注销逻辑。

8749

oauth2 单点登录_OAuth2实现单点登录SSO

1. 前言技术这东西吧,看别人写的好像很简单似的,到自己去写的时候就各种问题,“一看就会,一做就错”。网上关于实现SSO的文章一大堆,但是当你真的照着写的时候就会发现根本不是那么回事儿,简直让人抓狂,尤其是对于我这样的菜鸟。几经曲折,终于搞定了,决定记录下来,以便后续查看。先来看一下效果2. 准备2.1. 单点登录最常见的例子是,我们打开淘宝APP,首页就会有天猫、聚划算等服务的链接,当你点击以后...

weixin_39621075的博客 145
上一篇: oauth2资源服务配置及源码解析
下一篇: oauth2自定义登录页面
贝壳里的沙
博客等级 码龄18年 298粉丝 180原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

贝壳里的沙

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值