1. Theories

What is SSO?

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

What is Spring Security?

Spring Security官方解释

What is OAuth2?

首先简单了解一下用于验证及授权的OAuth协议,

OAuth(开放授权)是一个开放标准,允许用户让第三方应用访问该用户在某一网站上存储的私密的资源(如照片,视频,联系人列表),而无需将用户名和密码提供给第三方应用。

OAuth 2.0是该协议的2.0版本,2012年10月,OAuth 2.0协议正式发布为RFC 6749。下图是OAuth标准授权流程,即 Authorization Code Grant模式的示意图。

该方式的优缺点:

  • 优点:将登录逻辑从各应用处抽取出来,统一了登录入口。
  • 缺点:登录授权过程需要重定向多次,实现起来比较复杂。

SSO OAuth2.0登录授权流程梳理,如下图所示:

在选用OAuth2.0协议作为SSO的实现方式后,需要选定一个实现了该协议流程的框架来简化开发。经过比较后,最终选定Spring Security SSO。

由流程图可以得出,主要需要配置开发有三部分,分别为client、authorizetion server及resource server,由于resource server本身需要实现功能比较简单,所以authorizetion server和resource server本身可以是一个应用。

2. Coding

2.1 OverView

In this tutorial, we will discuss how to implement SSO – Single Sign On – using Spring Security OAuth and Spring Boot.
We will use three separate applications:
An Authorization Server – which is the central authentication mechanism
Two Client Applications: the applications using SSO
Very simply put, when a user tries to access a secured page in the client app, they will be redirected to authenticate first, via the Authentication Server.
And we are going to use the Authorization Code grant type out of OAuth2 to drive the delegation of authentication.

2.2 The Client App

Maven Dependencies

Let us start with our Client Application; we will, of course, use Spring Boot to minimize the configuration:

<dependencies><dependency><groupId>org.springframework.security.oauth.boot</groupId><artifactId>spring-security-oauth2-autoconfigure</artifactId><version>2.1.6.RELEASE</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><dependency><groupId>org.thymeleaf.extras</groupId><artifactId>thymeleaf-extras-springsecurity5</artifactId></dependency>
</dependencies>

Note that we should depend on thymeleaf-extras-springsecurity5 instead of thymeleaf-extras-springsecurity4, because only the version 5 matches the latest springboot and spring security version.

Boot Application

@SpringBootApplication
public class SsoApp1Application extends SpringBootServletInitializer {@Beanpublic RequestContextListener requestContextListener() {return new RequestContextListener();}public static void main(String[] args) {SpringApplication.run(SsoApp1Application.class, args);}}

Security Configuration

Next, the most important part, the security configuration of our client application:

@Configuration
@EnableOAuth2Sso
public class AppSecurityConfig extends WebSecurityConfigurerAdapter {@Overridepublic void configure(HttpSecurity http) throws Exception {http.antMatcher("/**").authorizeRequests().antMatchers("/login**").permitAll().anyRequest().authenticated();}
}

The core part of this configuration is, of course, the @EnableOAuth2Sso annotation we are using to enable Single Sign On.
Note that we need to extend the WebSecurityConfigurerAdapter – without it, all the paths will be secured – so the users will be redirected to log in when they try to access any page. In our case here, the index and login pages are the only pages that can be accessed without authentication.
Finally, we also defined a RequestContextListener bean to handle requests scopes.
Further reading:
When to use Spring Security`s antMatcher()?
Spring security application of antMatcher() vs. antMatchers()

WebMVC Configuration

@Configuration
@EnableWebMvc
public class AppWebConfig implements WebMvcConfigurer {@Overridepublic void addViewControllers(final ViewControllerRegistry registry) {registry.addViewController("/").setViewName("index");}
}

The method addViewController is overrided to map the root path “/” to the view “index”, here are some related links to explain it:
Java Spring Boot: How to map my app root (“/”) to index.html?
More details can be found at the source code of spring-webmvc.

application.yml

server:port: 8081servlet:session:cookie:name: APP1SESSION
security:oauth2:client:clientId: ssoIdclientSecret: ssoSecretaccessTokenUri: http://localhost:8080/oauth/tokenuserAuthorizationUri: http://localhost:8080/oauth/authorizeresource:userInfoUri: http://localhost:8080/user/me
spring:thymeleaf:cache: false

For app1 client, the port should be 8081, the session.cookie.name=APP1SESSION; app2 port: 8082, cookie name: APP2SESSION.
You may wonder why we must add cookies for each app, here is an answer from stackoverflow:

Centinul as you have figured out this happens due to a cookie conflict, unfortunately cookies don’t respect the port numbers. And so both Apps interfere with each other since both are setting JSESSIONID.
There are two easy workarounds:

  1. use server.context-path to move each App to different paths, note that you need to do this for both
  2. set the server.session.cookie.name for one App to something different, e.g., APPSESSIONID

I would suggest to put this workaround in a profile that you activate for localhost only.

Further reading:
Are HTTP cookies port specific?

Front End

resources/template/index.html

<!DOCTYPE html>
<html xmlns:th="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /><title>Spring Security SSO 1</title><link rel="stylesheet"href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css" />
</head><body>
<div class="container"><div class="col-sm-12"><h1>APP1</h1>Welcome, <span th:text="${#authentication.name}">Name</span></div>
</div>
</body>
</html>

2.3 The Auth Server

Maven Dependencies

<dependency><groupId>org.springframework.security.oauth</groupId><artifactId>spring-security-oauth2</artifactId><version>2.3.6.RELEASE</version>
</dependency>

OAuth Configuration

It is important to understand that we are going to run the Authorization Server and the Resource Server together here, as a single deployable unit.
Let us start with the configuration of our Resource Server – which doubles as our primary Boot application:

@SpringBootApplication
@EnableResourceServer
public class SsoServerApplication extends SpringBootServletInitializer {public static void main(String[] args) {SpringApplication.run(SsoServerApplication.class, args);}}

Then, we will configure our Authorization server:

@Configuration
@EnableAuthorizationServer
public class AuthorizationConfig extends AuthorizationServerConfigurerAdapter {@Autowiredprivate BCryptPasswordEncoder passwordEncoder;@Overridepublic void configure(AuthorizationServerSecurityConfigurer oauthServer) {oauthServer.tokenKeyAccess("permitAll()").checkTokenAccess("isAuthenticated()");}@Overridepublic void configure(ClientDetailsServiceConfigurer clients) throws Exception {clients.inMemory().withClient("ssoId").secret(passwordEncoder.encode("ssoSecret")).authorizedGrantTypes("authorization_code").scopes("user_info").autoApprove(true).redirectUris("http://localhost:8081/login", "http://localhost:8082/login");}
}

Note how we are only enabling a simple client using the authorization_code grant type.
Also, note how autoApprove is set to true so that we’re not redirected and promoted to manually approve any scopes.

Security Configuration

Now, let us move to the configuration and define a simple form login mechanism:

@Configuration
@Order(1)
public class ServerSecurityConfig extends WebSecurityConfigurerAdapter {@Overrideprotected void configure(HttpSecurity http) throws Exception {http.requestMatchers().antMatchers("/login", "/oauth/authorize").and().authorizeRequests().anyRequest().authenticated().and().formLogin().permitAll();}@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.inMemoryAuthentication().withUser("jake").password(passwordEncoder().encode("123")).roles("USER");}@Beanpublic BCryptPasswordEncoder passwordEncoder(){return new BCryptPasswordEncoder();}
}

The @Order(1) annotation grants ServerSecurityConfig the highest priority among all Security Configurations.
Note that we used simple in-memory authentication, but we can simply replace it with a custom userDetailsService.

User Endpoint

Finally, we will create our user endpoint we used earlier in our configuration:

@RestController
public class UserController {@GetMapping("/user/me")public Principal user(Principal principal) {return principal;}
}

3. Conclusion

The SSO system sometimes meets the unauthorized problem that when visiting app1 index(http://localhost:8081), obviously it will redirects to server login page(http://localhost:8080/login), and then successfully login; then the page will redirects to the app1 index again. All the steps are under control till now. But when you visit app2 index(http://localhost:8082), strange thing happens, the prompt error messages(e.g. statusCode=999) remind you that app2 index is not authorized(statusCode=401) yet.
In this quick tutorial, we focused on implementing Single Sign On using Spring Security Oauth2 and Spring Boot.
The full source code can be downloaded from Gitee.

基于Spring Security OAuth2的SSO(单点登录)相关推荐

  1. java oauth sso 源码_基于Spring Security Oauth2的SSO单点登录+JWT权限控制实践

    概 述 在前文<基于Spring Security和 JWT的权限系统设计>之中已经讨论过基于 Spring Security和 JWT的权限系统用法和实践,本文则进一步实践一下基于 Sp ...

  2. 基于Spring Security + OAuth2 的SSO单点登录(服务端)

    相关技术 spring security: 用于安全控制的权限框架 OAuth2: 用于第三方登录认证授权的协议 JWT:客户端和服务端通信的数据载体 传统登录 登录web系统后将用户信息保存在ses ...

  3. 使用Spring Secuirty Oauth2实现SSO单点登录

    文章目录 1. 什么是单点登录 2. 微服务架构下单点登录的思路 3. 使用 Spring Secuirty Oauth2 实现SSO单点登录 ①:建表 ②:授权服务器逻辑 ③:网关逻辑 4. 接口测 ...

  4. 基于Spring Security与JWT实现单点登录

    基于RBAC的权限管理 RBAC(Role-Based Access Control):基于角色的访问控制 当前项目中,RBAC具体的表现为: 管理员表:ams_admin 角色表:ams_role ...

  5. 前后端分离基于Oauth2的SSO单点登录怎样做?

    一.说明 单点登录顾名思义就是在多个应用系统中,只需要登录一次,就可以访问其他相互信任的应用系统,免除多次登录的烦恼:本文主要介绍跨域间的 前后端分离 项目怎样实现单点登录,并且与 非前后端分离 的差 ...

  6. 基于 Spring Security OAuth2和 JWT 构建保护微服务系统

    我们希望自己的微服务能够在用户登录之后才可以访问,而单独给每个微服务单独做用户权限模块就显得很弱了,从复用角度来说是需要重构的,从功能角度来说,也是欠缺的.尤其是前后端完全分离之后,我们的用户信息不一 ...

  7. 基于Spring Security的AJAX请求需要登录的解决方案

    基于Spring Security的AJAX请求需要登录的解决方案 参考文章: (1)基于Spring Security的AJAX请求需要登录的解决方案 (2)https://www.cnblogs. ...

  8. Eurynome Cloud Athena 基于Spring Security OAuth2 的前后端分离脚手架

    Eurynome Cloud Athena 是什么? Eurynome Cloud Athena 是从 Eurynome Cloud 中提取出来的.可以独立运行的.基于OAuth2认证的.前后端分离的 ...

  9. Spring Cloud云架构 - SSO单点登录之OAuth2.0登录流程(2)

    上一篇是站在巨人的肩膀上去研究OAuth2.0,也是为了快速帮助大家认识OAuth2.0,闲话少说,我根据框架中OAuth2.0的使用总结,画了一个简单的流程图(根据用户名+密码实现OAuth2.0的 ...

最新文章

  1. 自动驾驶汽车事故的罪责追究
  2. 007_Maven依赖管理
  3. php字符串学习笔记
  4. C++开发即时通讯软件,需要注意什么?
  5. access mysql连接字符串_[数据库连接字符串] Access 连接字符串
  6. 如何选择合适的数据可视化BI工具
  7. 八.创建型设计模式——Singleton Pattern(单例模式)
  8. 颜色空间——Gamma与线性颜色空间
  9. Project Aposs
  10. java mysql 多表查询_MySQL必备知识多表查询
  11. java中数组下标越界对应的异常类是_Java 常见异常种类
  12. 电子设计教程48:流水灯电路-完整电路设计
  13. 【博客33】使用 “NVI“解决缺省参数绑定问题
  14. 华为鸿蒙内存机制,华为鸿蒙系统对内存有要求吗?
  15. 深度长文:我对CQRS/EventSourcing架构的思考
  16. MyBatis 配置 settings 标签
  17. 多多情报通:拼多多修改关键词会降权吗?有什么影响?
  18. 使IE浏览器支持webp格式图片显示
  19. 用什么软件工具可以一键添加渐入效果同时虚化边框背景呢?
  20. ***菜鸟要学会的几个cmd ddos命令

热门文章

  1. Sandboxie-沙箱软件-Plus版本(Qt)-主框架程序-SandMan.exe-创建语言文件-tr-Qt-语言国际化
  2. APP测试面试题要点
  3. DevOps深度分享:从项目制到产品制实现价值传递
  4. 华为机试真题 C++ 实现【免单统计】
  5. iPhone8 plus拍的照片怎么打开 heic格式如何查看
  6. 4月4号服务器维护什么时候结束,绝地求生维护更新到什么时候结束 4月28日正式服维护公告一览...
  7. 深度学习怎么入门?一文弄清楚最常见的专业词汇
  8. Hebb学习规则 以及 Hebb网络
  9. 一探究竟:安信可模组ESP32-SU、ESP32-SL和ESP32-S对比,区别在哪里?
  10. 什么是DCIM数据中心管理系统