zxpnet网站 zxpnet网站
首页
前端
后端服务器
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

zxpnet

一个爱学习的java开发攻城狮
首页
前端
后端服务器
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 大后端课程视频归档
  • 南航面试题
  • 并发编程

  • 性能调优

  • java8语法

  • lombok

  • 日志

  • 工具类

  • spring

  • mybatis

  • springboot

  • redis

  • zookeeper

  • springcloud

  • dubbo

  • netty

  • springsecurity

    • springsecurity基本使用及个性化登录配置
    • 微服务安全与实战

    • springsecurity认证流程分析
    • springsecurity过滤器链分析
    • springsecurity图形验证码
    • springsecurity记住我RememberMe功能
    • springsecurity手机验证码登陆
    • springsecurity httpFirewall
    • springsecurity漏洞保护
    • Untitled
    • 跨域问题
    • Springsecurity Session会话管理
    • springsecurity控制授权
    • 权限模型
    • springseuciryt oauth2基础
    • 基于JWT实现SSO单点登录
      • 实现原理
      • 代码结构
      • 认证服务器开发
      • 应用A
        • 改善代码
        • 1.设置表单登录
        • 2.重写授权的逻辑
    • springsocial第三方登陆
    • springsecurity退出登陆
    • Spring security 集成 JustAuth 实现第三方授权登录
    • Spring Boot+CAS 单点登录
    • Springsecurity常见错误
  • mq消息中间件

  • shiro

  • beetle

  • 模板引擎

  • jpa

  • 数据结构与算法

  • 数据库知识与设计

  • gradle

  • maven

  • bus

  • 定时任务

  • docker

  • centos

  • 加解密

  • biz业务

  • pigx项目

  • 开源项目

  • 品达通用权限项目-黑马

  • 货币交易项目coin-尚学堂

  • php

  • backend
  • springsecurity
shollin
2021-07-13
目录

基于JWT实现SSO单点登录

  • 实现原理
  • 代码结构
  • 认证服务器开发
  • 应用A
    • 改善代码
    • 1.设置表单登录
    • 2.重写授权的逻辑

# 实现原理

用户访问应用A 时,需要到认证服务上进行认证。认证成功后,再去访问应用B,仍然需要到认证服务器认证,只不过这次不用再重新登录了,只需要授一下权就可以了。

# 代码结构

sso-server :认证服务器 sso-client1:应用A sso-client2:应用B

  <groupId>com.imooc.sso</groupId>
    <artifactId>sso-demo</artifactId>
    <version>1.0-SNAPSHOT</version>
    <modules>
        <module>sso-server</module>
        <module>sso-client1</module>
        <module>sso-client2</module>
    </modules>
    <packaging>pom</packaging>
    <description>单点登录demo</description>
1
2
3
4
5
6
7
8
9
10

# 认证服务器开发

1.配置客户端的 clientId 和 clientSecert, 设置JwtToekenStore

2.配置用户密码

@Configuration
@EnableAuthorizationServer
public class SsoAuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
	
	@Override
	public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
		clients.inMemory()
				.withClient("imooc1")
				.secret("imoocsecrect1")
				.authorizedGrantTypes("authorization_code", "refresh_token")
				.scopes("all")
				.and()
				.withClient("imooc2")
				.secret("imoocsecrect2")
				.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 {
		security.tokenKeyAccess("isAuthenticated()");//访问需认证,默认denyAll
	}
	
	@Bean
	public TokenStore jwtTokenStore() {
		return new JwtTokenStore(jwtAccessTokenConverter());
	}
	
	@Bean
	public JwtAccessTokenConverter jwtAccessTokenConverter(){
		JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setSigningKey("imooc");
        return converter;
	}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
server.port = 9999
server.context-path = /server
security.user.password = 123456
1
2
3

# 应用A

1、@EnableOAuth2Sso

2、添加配置文件

3、写一个简单的前端

@SpringBootApplication
@RestController
@EnableOAuth2Sso
public class SsoClient1Application {
	
	@GetMapping("/user")
	public Authentication user(Authentication user) {
		return user;
	}

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
security.oauth2.client.clientId = imooc1
security.oauth2.client.clientSecret = imoocsecrect1
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
security.oauth2.resource.jwt.key-uri = http://127.0.0.1:9999/server/oauth/token_key

server.port = 8080
server.context-path = /client1
1
2
3
4
5
6
7
8
<!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>

1
2
3
4
5
6
7
8
9
10
11
12

应用B和应用A类似,这样一个简单的SSO Demo就搭建好了。启动来看一下

我们先来访问应用A

会直接引导我们跳转到认证服务器上,输入用户名和密码后,跳转到授权界面 在这里插入图片描述 点击授权,完成登录 在这里插入图片描述 此时我们点击 访问Client2,同样会跳转到认证服务器,但是这次不用登录,直接授权就可以了 在这里插入图片描述

# 改善代码

  • 登录方法改为表单登录
  • 省去用户去授权,默认直接授权

# 1.设置表单登录

@Configuration
public class SsoSecurityConfig extends WebSecurityConfigurerAdapter {
	
	@Autowired
	private UserDetailsService userDetailsService;
	
	@Bean
	public PasswordEncoder passwordEncoder()	{
		return new BCryptPasswordEncoder();
	}
	
	@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
	}
	
	@Override
	protected void configure(HttpSecurity http) throws Exception {
		http.formLogin().and().authorizeRequests().anyRequest().authenticated();
	}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# 2.重写授权的逻辑

隐藏授权界面,并直接提交,参考源码:WhitelabelApprovalEndpoint

@FrameworkEndpoint  // 相当于restController
@SessionAttributes("authorizationRequest")
public class WhitelabelApprovalEndpoint {

	@RequestMapping("/oauth/confirm_access")
	public ModelAndView getAccessConfirmation(Map<String, Object> model, HttpServletRequest request) throws Exception {
	}	
}
1
2
3
4
5
6
7
8
@RestController
@SessionAttributes("authorizationRequest")
public class SsoApprovalEndpoint {
	
	@RequestMapping("/oauth/confirm_access")
	public ModelAndView getAccessConfirmation(Map<String, Object> model, HttpServletRequest request) throws Exception {
		String template = createTemplate(model, request);
		if (request.getAttribute("_csrf") != null) {
			model.put("_csrf", request.getAttribute("_csrf"));
		}
		return new ModelAndView(new SsoSpelView(template), model);
	}

	protected String createTemplate(Map<String, Object> model, HttpServletRequest request) {
		String template = TEMPLATE;
		if (model.containsKey("scopes") || request.getAttribute("scopes") != null) {
			template = template.replace("%scopes%", createScopes(model, request)).replace("%denial%", "");
		}
		else {
			template = template.replace("%scopes%", "").replace("%denial%", DENIAL);
		}
		if (model.containsKey("_csrf") || request.getAttribute("_csrf") != null) {
			template = template.replace("%csrf%", CSRF);
		}
		else {
			template = template.replace("%csrf%", "");
		}
		return template;
	}

	private CharSequence createScopes(Map<String, Object> model, HttpServletRequest request) {
		StringBuilder builder = new StringBuilder("<ul>");
		@SuppressWarnings("unchecked")
		Map<String, String> scopes = (Map<String, String>) (model.containsKey("scopes") ? model.get("scopes") : request
				.getAttribute("scopes"));
		for (String scope : scopes.keySet()) {
			String approved = "true".equals(scopes.get(scope)) ? " checked" : "";
			String denied = !"true".equals(scopes.get(scope)) ? " checked" : "";
			String value = SCOPE.replace("%scope%", scope).replace("%key%", scope).replace("%approved%", approved)
					.replace("%denied%", denied);
			builder.append(value);
		}
		builder.append("</ul>");
		return builder.toString();
	}

	private static String CSRF = "<input type='hidden' name='${_csrf.parameterName}' value='${_csrf.token}' />";

	private static String DENIAL = "<form id='denialForm' name='denialForm' action='${path}/oauth/authorize' method='post'><input name='user_oauth_approval' value='false' type='hidden'/>%csrf%<label><input name='deny' value='Deny' type='submit'/></label></form>";

	private static String TEMPLATE = "<html><body><div style='display:none;'><h1>OAuth Approval</h1>"
			+ "<p>Do you authorize '${authorizationRequest.clientId}' to access your protected resources?</p>"
			+ "<form id='confirmationForm' name='confirmationForm' action='${path}/oauth/authorize' method='post'><input name='user_oauth_approval' value='true' type='hidden'/>%csrf%%scopes%<label><input name='authorize' value='Authorize' type='submit'/></label></form>"
			+ "%denial%</div><script>document.getElementById('confirmationForm').submit()</script></body></html>";

	private static String SCOPE = "<li><div class='form-group'>%scope%: <input type='radio' name='%key%'"
			+ " value='true'%approved%>Approve</input> <input type='radio' name='%key%' value='false'%denied%>Deny</input></div></li>";


}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60

参考文章:

基于JWT实现SSO单点登录_暴走猿人的博客-CSDN博客_jwt认证实现单点登录原理 (opens new window)

springseuciryt oauth2基础
springsocial第三方登陆

← springseuciryt oauth2基础 springsocial第三方登陆→

最近更新
01
国际象棋
09-15
02
成语
09-15
03
自然拼读
09-15
更多文章>
Theme by Vdoing | Copyright © 2019-2023 zxpnet | 粤ICP备14079330号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式