SpringBoot集成SpringSecurity5和OAuth2 — 2、OAuth2客户端【SSO单点登录时作为客户端】

相关代码:参考码云上的sso-client【仅供本人参考使用,不对外,但跟着本文也可实现】

注意:OAuth2的客户端和资源服务器是不同的概念,OAuth2的客户端类似于第三方应用,它对接OAuth2服务器主要是使用OAuth2服务器端提供的客户信息【此处是OAuth2给用户分配的token】来登录客户端自己的系统而非资源服务器,后续用户访问的资源也是客户端自己存的用户资源,所以使用客户端的code来请求token会报错;而资源服务器则需要单独获取code后再获取token,然后才能请求资源服务器上的资源。【资源服务器和认证授权服务器同属一方。】

获取授权码:

http://localhost:8080/oauth/authorize?response_type=code&client_id=client&scope=read&redirect_uri=http://localhost:8090/callback

获取token令牌:

http://localhost:8080/oauth/token?grant_type=authorization_code&code=F4eRew&client_id=client&client_secret=secret&redirect_uri=http://localhost:8090/callback

一、项目环境配置

1.1 、JDK8

1.2、Spring Boot: 2.3.0.RELEASE

1.3、spring-cloud.version:Hoxton.SR4

二、pom.xml

1、加入thymeleaf模板依赖(可选)

		<!-- thymeleaf模板-->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-thymeleaf</artifactId>
		</dependency>

2、加入starter-web依赖

		<!-- starter-web -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

3、加入oauth2依赖

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

4、完整的pom.xml文件

<?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.0.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>security.oauth2</groupId>
	<artifactId>client</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>client</name>
	<description>OAuth2的客户端</description>

	<properties>
		<java.version>1.8</java.version>
		<spring-cloud.version>Hoxton.SR4</spring-cloud.version>
	</properties>

	<dependencies>

		<!-- thymeleaf模板-->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-thymeleaf</artifactId>
		</dependency>

		<!-- starter-web -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

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

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
			<exclusions>
				<exclusion>
					<groupId>org.junit.vintage</groupId>
					<artifactId>junit-vintage-engine</artifactId>
				</exclusion>
			</exclusions>
		</dependency>

	</dependencies>

	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.springframework.cloud</groupId>
				<artifactId>spring-cloud-dependencies</artifactId>
				<version>${spring-cloud.version}</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>

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

</project>

三、application.properties

1、配置客户端端口

server.port=8090

因为我在OAuth2认证服务器上写的回调地址的端口是8090,所以此处设置成8090端口

2、配置登陆路径

security.oauth2.sso.login-path=/callback

因为我在OAuth2认证服务器上写的回调地址是callback,所以此处设置成callback,必须和认证服务器上设置的回调地址一致,否则会报错

3、配置客户端在认证服务器上的appId和appSecret

#客户端id
security.oauth2.client.client-id=client
#客户端secret
security.oauth2.client.client-secret=secret

4、配置授权码的地址

#获取授权码的地址
security.oauth2.client.user-authorization-uri=http://localhost:8080/oauth/authorize

5、配置获取token的地址

security.oauth2.client.access-token-uri=http://localhost:8080/oauth/token

6、配置校验token的地址

security.oauth2.resource.token-info-uri=http://localhost:8080/oauth/check_token

7、配置客户端的session的cookie名称(必须配置)

server.servlet.session.cookie.name=OAuth-Client

原因:如果同一台服务器(或电脑)上,同时部署了多个Spring Boot项目(假设A、B项目),但是项目之间没有任何关系,相互独立。然后在同一个浏览器上,同时请求A、B项目上的服务,在A项目进行定时认证session时,前端在发起Request请求时常将B的sessionId 当做A的sessionId上传给A,而导致A项目验证session时失败。

出现这种情况的原因是:A、B项目存储SessionId 的cookie name 相同,浏览器傻傻分不清JSESSIONID到底是A还是B的,所以就拿错了。

解决办法:在A项目的application.properties 中设置session的cookie名称,与B项目的session的cookie名称不相同即可

8、完整的application.properties配置


server.port=8090

spring.application.name=sso-client

#登陆路径
#security.oauth2.sso.login-path=/login
security.oauth2.sso.login-path=/callback
#客户端id
security.oauth2.client.client-id=client
#客户端secret
security.oauth2.client.client-secret=secret
#获取授权码的地址
security.oauth2.client.user-authorization-uri=http://localhost:8080/oauth/authorize
#获取token的地址
security.oauth2.client.access-token-uri=http://localhost:8080/oauth/token
#校验token的地址
security.oauth2.resource.token-info-uri=http://localhost:8080/oauth/check_token


#禁止同名的sessionId,是个坑,不加会报错
server.servlet.session.cookie.name=OAuth-Client

spring.thymeleaf.prefix = classpath:/templates/
#spring.thymeleaf.suffix = .html
spring.thymeleaf.encoding = UTF-8
spring.thymeleaf.servlet.content-type=text/html
spring.thymeleaf.cache = false

四、在springboot的启动器上开启SSOclient的配置

加入注解:@EnableOAuth2Sso

五、新建一个Controller

package security.oauth2.client.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class ClientController {

    @GetMapping("/callback")
    public String callback(){
        return "SSO Client callback!";
    }

    @GetMapping("/oauth-client")
    public String oauthClient(){
        return "This is SSO Client!";
    }


}

六、测试

1、在浏览器中输入:localhost:8090/oauth-client后,跳转到了认证服务器的登录界面,同时Set-Cookie设置的cookie名称也从JSESSIONID变为了OAuth-Client。

2、输入用户名密码后,进入是否同意授权的界面

3、选择同意授权后,跳回原来浏览器请求的客户端的地址,同时在服务器给出的回调地址中可以查看到认证服务器返回的授权码code

七、SSO单点登录时作为客户端

7.1 新增WebSecurityConfig:

       主要是为了增加退出客户端的操作,在退出客户端系统时,发请求退出OAuth2认证服务器,这样就能实现单点登录的退出,即从所有系统中退出;

package security.oauth2.client.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;

@EnableOAuth2Sso
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/bootstrap/**");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.logout().logoutSuccessUrl("http://localhost:8080/logout")
                .and()
                .authorizeRequests()
                .anyRequest().authenticated()
                .and()
                .csrf().disable();
    }
}

注意

1、将 @EnableOAuth2Sso 注解从启动类上改到WebSecurityConfig上;

2、退出操作,请求:http://localhost:8090/logout【logout:是security默认的退出请求地址】