Spring Security (14): Realize SSO single sign-on based on JWT

The previous article introduced the composition of JWT and the actual application of JWT as a request token, including token creation, token parsing and so on. This article will discuss another JWT application scenario, based on JWT to achieve SSO single sign-on.

What is SSO single sign-on?

Scene demonstration

  • First, you can open Taobao (domain name: https://www.taobao.com/), as follows, and then click the Tmall button on the Taobao page to open the Tmall page (domain name: https://www.tmall.com/). At this time, you are not logged in. Choose login to jump to the login page, and you will find that the domain name is another :https://login.taobao.com/
  • After logging in, it will automatically jump back to the Taobao homepage, reopen Tmall (or refresh the Tmall page) and find that Tmall has also logged in.
    Taobao Home
  • Once you log in, server resources under multiple domains can be accessed. This is single sign-on.

Flow chart explanation

  • First, you need to have multiple application servers (two are used here for demonstration), and an authentication server is also required.
  • Illustrated description:
    Flow chart explanation

Flow Description

  • As shown in the figure above, application A is equivalent to Taobao, application B is equivalent to Tmall, and the authentication server is equivalent to the server where the login page is redirected.
  • When requesting access to application A, application A will request authorization from the authentication server, and the user will perform authentication and authorization on the authentication server.
  • After the authorization is successful, the authorization code will be returned to the application A, and the application A will request an access token. The authentication server will generate and return the JWT token to the application A after the authorization code is successfully verified.
  • Application A can hold JWT token for resource access (as of now, the above steps are from step 0 to step 6 标准的OAuth2.0授权码模式的登录流程).
  • In the case where application A jumps to application B or directly opens a new page of application B, application B is still in an unauthorized state, and it will also request authorization from the authentication server.
  • Because application A has been authorized to log in, and the authentication server can identify application B (the authentication server has been configured), it is no longer necessary to log in to application B again. The authentication server will complete the OAth process and return a new JWT to application B. This Then application B can access server resources.

achieve

  • Note that the above process is the first 基于Http调度, and 不局限于使用什么技术栈实现, even 不局限于语言, Java or PHP or other languages can be realized. Next, we will use Spring Security OAuththis technology stack to implement a simple single sign-on.

Create an authentication server

  • Module name:sso-server
  • Introduced packages
	<dependencies>
		<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.security.oauth</groupId>
			<artifactId>spring-security-oauth2</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-jwt</artifactId>
		</dependency>
	</dependencies>
  • Create OAuth authentication server configuration, inherit AuthorizationServerConfigurerAdapter
@Configuration
@EnableAuthorizationServer
public class SsoAuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
    
    
	
	@Override
	public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
    
    
		clients.inMemory()
				// 配置将会给到 meicloud1 应用发令牌
				.withClient("meicloud1")
				.secret("meicloudsecrect1")
				// 授权类型和授权码
				.authorizedGrantTypes("authorization_code", "refresh_token")
				.scopes("all")
				.and()
				// 配置将会给到 meicloud2 应用发令牌
				.withClient("meicloud2")
				.secret("meicloudsecrect2")
				.authorizedGrantTypes("authorization_code", "refresh_token")
				.scopes("all");
	}
	
	@Override
	public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
    
    
		endpoints.tokenStore(jwtTokenStore()).accessTokenConverter(jwtAccessTokenConverter());
	}

	@Override
	public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
    
    
		// Spring Security的授权表达式,意思是要访问认证服务器的tokenKey的时候需要经过身份认证,这里即下面给JWT签名用的秘钥
		security.tokenKeyAccess("isAuthenticated()");
	}
	
	@Bean
	public TokenStore jwtTokenStore() {
    
    
		return new JwtTokenStore(jwtAccessTokenConverter());
	}
	
	@Bean
	public JwtAccessTokenConverter jwtAccessTokenConverter(){
    
    
		JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
		// 给JWT签名用的秘钥
        converter.setSigningKey("meicloud");
        return converter;
	}
}
  • application.properties file
# 端口
server.port = 9999
# context-path
server.context-path = /server
# 配置user用户,密码是123456,用户A跳到认证服务器的时候需要输入用户名密码登录
security.user.password = 123456

Create application A

  • Module name:sso-client1
  • The dependent package is the same as the authentication server.
  • Start class
@SpringBootApplication
@RestController
// 让SSO可以生效的注解
@EnableOAuth2Sso
public class SsoClient1Application {
    
    
	
	@GetMapping("/user")
	public Authentication user(Authentication user) {
    
    
		return user;
	}

	public static void main(String[] args) {
    
    
		SpringApplication.run(SsoClient1Application.class, args);
	}
	
}
  • Add application.properties configuration file
# 应用A,对应认证服务器配置的应用1
security.oauth2.client.clientId = meicloud1
security.oauth2.client.clientSecret = meicloudsecrect1
# 当请求需要认证时,要跳转到认证服务器的地址
security.oauth2.client.user-authorization-uri = http://127.0.0.1:9999/server/oauth/authorize
# 配置向哪个地址去请求令牌
security.oauth2.client.access-token-uri = http://127.0.0.1:9999/server/oauth/token
# 获取认证服务器JWT秘钥的接口,应用服务器需要获取秘钥解析令牌的正确性
security.oauth2.resource.jwt.key-uri = http://127.0.0.1:9999/server/oauth/token_key

server.port = 8080
server.context-path = /client1
  • Create an access page index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>SSO Client1</title>
</head>
<body>
	<h1>SSO Demo Client1</h1>
	<a href="http://127.0.0.1:8060/client2/index.html">访问Client2</a>
</body>
</html>

Create application B

  • Overall and application A are the same, pay attention to modify the configuration file
# 应用B,对应认证服务器配置的应用2
security.oauth2.client.clientId = meicloud2
security.oauth2.client.clientSecret = meicloudsecrect2
# 当请求需要认证时,要跳转到认证服务器的地址
security.oauth2.client.user-authorization-uri = http://127.0.0.1:9999/server/oauth/authorize
# 配置向哪个地址去请求令牌
security.oauth2.client.access-token-uri = http://127.0.0.1:9999/server/oauth/token
# 获取认证服务器JWT秘钥的接口,应用服务器需要获取秘钥解析令牌的正确性
security.oauth2.resource.jwt.key-uri = http://127.0.0.1:9999/server/oauth/token_key

server.port = 8060
server.context-path = /client2

Single sign-on effect demo

  • First start Server, then start Client1 and Client2
  • Visit the index.html of Client1 (http://127.0.0.1:8080/client1/index.html), it will automatically jump to the configured authentication server for authentication and authorization
    certified
  • Use the user name and password (the authentication server has been configured: user, 123456) to log in, at this time you have logged in and jump back to Client1
    Client1
  • You can check the currently logged-in user (http://127.0.0.1:8080/client1/user), you will find that the current user is user
    Current user
  • You can click to visit Client2 on the Client1 page, and then you will directly jump to the Client2 page without logging in.
    Client2
  • The same can view the current user, which is also user, but the token string of JWT is different.
    View users

to sum up

  • The above is a simple demonstration of SSO single sign-on.
  • Some detailed thinking, for example, when the application accesses the resource server, how does the resource server know that its JWT token is correct? In fact, the main thing to judge whether the JWT token is valid is 获取到认证服务器对JWT进行签名的秘钥to verify the JWT through the secret key.
  • In fact, you will find that a complete set of Spring Security OAuthsingle sign-on has been implemented without writing a few lines of code. The configuration is also very simple. Of course, it should be used flexibly in our projects and must not be solidified.

Guess you like

Origin blog.csdn.net/qq_36221788/article/details/106893416