1.  前言

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

2.  准备

2.1.  单点登录

最常见的例子是,我们打开淘宝APP,首页就会有天猫、聚划算等服务的链接,当你点击以后就直接跳过去了,并没有让你再登录一次

下面这个图是我再网上找的,我觉得画得比较明白:

可惜有点儿不清晰,于是我又画了个简版的:

重要的是理解:

  • SSO服务端和SSO客户端直接是通过授权以后发放Token的形式来访问受保护的资源
  • 相对于浏览器来说,业务系统是服务端,相对于SSO服务端来说,业务系统是客户端
  • 浏览器和业务系统之间通过会话正常访问
  • 不是每次浏览器请求都要去SSO服务端去验证,只要浏览器和它所访问的服务端的会话有效它就可以正常访问

2.2.  OAuth2

推荐以下几篇博客

OAuth 2.0

Spring Security对OAuth2的支持

3.  利用OAuth2实现单点登录

接下来,只讲跟本例相关的一些配置,不讲原理,不讲为什么

众所周知,在OAuth2在有授权服务器、资源服务器、客户端这样几个角色,当我们用它来实现SSO的时候是不需要资源服务器这个角色的,有授权服务器和客户端就够了。

授权服务器当然是用来做认证的,客户端就是各个应用系统,我们只需要登录成功后拿到用户信息以及用户所拥有的权限即可

之前我一直认为把那些需要权限控制的资源放到资源服务器里保护起来就可以实现权限控制,其实是我想错了,权限控制还得通过Spring Security或者自定义拦截器来做

3.1.  Spring Security 、OAuth2、JWT、SSO

在本例中,一定要分清楚这几个的作用

首先,SSO是一种思想,或者说是一种解决方案,是抽象的,我们要做的就是按照它的这种思想去实现它

其次,OAuth2是用来允许用户授权第三方应用访问他在另一个服务器上的资源的一种协议,它不是用来做单点登录的,但我们可以利用它来实现单点登录。在本例实现SSO的过程中,受保护的资源就是用户的信息(包括,用户的基本信息,以及用户所具有的权限),而我们想要访问这这一资源就需要用户登录并授权,OAuth2服务端负责令牌的发放等操作,这令牌的生成我们采用JWT,也就是说JWT是用来承载用户的Access_Token的

最后,Spring Security是用于安全访问的,这里我们我们用来做访问权限控制

4.  认证服务器配置

4.1.  Maven依赖

<?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 http://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.1.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.cjs.sso</groupId>
<artifactId>oauth2-sso-auth-server</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>oauth2-sso-auth-server</name> <properties>
<java.version>1.8</java.version>
</properties> <dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth.boot</groupId>
<artifactId>spring-security-oauth2-autoconfigure</artifactId>
<version>2.1.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency> <dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.8.1</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.56</version>
</dependency> </dependencies> <build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build> </project>

这里面最重要的依赖是:spring-security-oauth2-autoconfigure

4.2.  application.yml

spring:
datasource:
url: jdbc:mysql://localhost:3306/permission
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
jpa:
show-sql: true
session:
store-type: redis
redis:
host: 127.0.0.1
password: 123456
port: 6379
server:
port: 8080

4.3. AuthorizationServerConfig(重要)

package com.cjs.sso.config;

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.security.core.token.DefaultToken;
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.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore; import javax.sql.DataSource; /**
* @author ChengJianSheng
* @date 2019-02-11
*/
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { @Autowired
private DataSource dataSource; @Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security.allowFormAuthenticationForClients();
security.tokenKeyAccess("isAuthenticated()");
} @Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.jdbc(dataSource);
} @Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.accessTokenConverter(jwtAccessTokenConverter());
endpoints.tokenStore(jwtTokenStore());
// endpoints.tokenServices(defaultTokenServices());
} /*@Primary
@Bean
public DefaultTokenServices defaultTokenServices() {
DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
defaultTokenServices.setTokenStore(jwtTokenStore());
defaultTokenServices.setSupportRefreshToken(true);
return defaultTokenServices;
}*/ @Bean
public JwtTokenStore jwtTokenStore() {
return new JwtTokenStore(jwtAccessTokenConverter());
} @Bean
public JwtAccessTokenConverter jwtAccessTokenConverter() {
JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
jwtAccessTokenConverter.setSigningKey("cjs"); // Sets the JWT signing key
return jwtAccessTokenConverter;
} }

说明:

  1. 别忘了@EnableAuthorizationServer
  2. Token存储采用的是JWT
  3. 客户端以及登录用户这些配置存储在数据库,为了减少数据库的查询次数,可以从数据库读出来以后再放到内存中

4.4.  WebSecurityConfig(重要)

package com.cjs.sso.config;

import com.cjs.sso.service.MyUserDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder; /**
* @author ChengJianSheng
* @date 2019-02-11
*/
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired
private MyUserDetailsService userDetailsService; @Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
} @Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/assets/**", "/css/**", "/images/**");
} @Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin()
.loginPage("/login")
.and()
.authorizeRequests()
.antMatchers("/login").permitAll()
.anyRequest()
.authenticated()
.and().csrf().disable().cors();
} @Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
} }

4.5.  自定义登录页面(一般来讲都是要自定义的)

package com.cjs.sso.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping; /**
* @author ChengJianSheng
* @date 2019-02-12
*/
@Controller
public class LoginController { @GetMapping("/login")
public String login() {
return "login";
} @GetMapping("/")
public String index() {
return "index";
} }

自定义登录页面的时候,只需要准备一个登录页面,然后写个Controller令其可以访问到即可,登录页面表单提交的时候method一定要是post,最重要的时候action要跟访问登录页面的url一样

千万记住了,访问登录页面的时候是GET请求,表单提交的时候是POST请求,其它的就不用管了

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Ela Admin - HTML5 Admin Template</title>
<meta name="description" content="Ela Admin - HTML5 Admin Template">
<meta name="viewport" content="width=device-width, initial-scale=1"> <link type="text/css" rel="stylesheet" th:href="@{/assets/css/normalize.css}">
<link type="text/css" rel="stylesheet" th:href="@{/assets/bootstrap-4.3.1-dist/css/bootstrap.min.css}">
<link type="text/css" rel="stylesheet" th:href="@{/assets/css/font-awesome.min.css}">
<link type="text/css" rel="stylesheet" th:href="@{/assets/css/style.css}"> </head>
<body class="bg-dark"> <div class="sufee-login d-flex align-content-center flex-wrap">
<div class="container">
<div class="login-content">
<div class="login-logo">
<h1 style="color: #57bf95;">欢迎来到王者荣耀</h1>
</div>
<div class="login-form">
<form th:action="@{/login}" method="post">
<div class="form-group">
<label>Username</label>
<input type="text" class="form-control" name="username" placeholder="Username">
</div>
<div class="form-group">
<label>Password</label>
<input type="password" class="form-control" name="password" placeholder="Password">
</div>
<div class="checkbox">
<label>
<input type="checkbox"> Remember Me
</label>
<label class="pull-right">
<a href="#">Forgotten Password?</a>
</label>
</div>
<button type="submit" class="btn btn-success btn-flat m-b-30 m-t-30" style="font-size: 18px;">登录</button>
</form>
</div>
</div>
</div>
</div> <script type="text/javascript" th:src="@{/assets/js/jquery-2.1.4.min.js}"></script>
<script type="text/javascript" th:src="@{/assets/bootstrap-4.3.1-dist/js/bootstrap.min.js}"></script>
<script type="text/javascript" th:src="@{/assets/js/main.js}"></script> </body>
</html>

4.6.  定义客户端

4.7.  加载用户

登录账户

package com.cjs.sso.domain;

import lombok.Data;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User; import java.util.Collection; /**
* 大部分时候直接用User即可不必扩展
* @author ChengJianSheng
* @date 2019-02-11
*/
@Data
public class MyUser extends User { private Integer departmentId; // 举个例子,部门ID private String mobile; // 举个例子,假设我们想增加一个字段,这里我们增加一个mobile表示手机号 public MyUser(String username, String password, Collection<? extends GrantedAuthority> authorities) {
super(username, password, authorities);
} public MyUser(String username, String password, boolean enabled, boolean accountNonExpired, boolean credentialsNonExpired, boolean accountNonLocked, Collection<? extends GrantedAuthority> authorities) {
super(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities);
}
}

加载登录账户

package com.cjs.sso.service;

import com.alibaba.fastjson.JSON;
import com.cjs.sso.domain.MyUser;
import com.cjs.sso.entity.SysPermission;
import com.cjs.sso.entity.SysUser;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils; import java.util.ArrayList;
import java.util.List; /**
* @author ChengJianSheng
* @date 2019-02-11
*/
@Slf4j
@Service
public class MyUserDetailsService implements UserDetailsService { @Autowired
private PasswordEncoder passwordEncoder; @Autowired
private UserService userService; @Autowired
private PermissionService permissionService; @Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
SysUser sysUser = userService.getByUsername(username);
if (null == sysUser) {
log.warn("用户{}不存在", username);
throw new UsernameNotFoundException(username);
}
List<SysPermission> permissionList = permissionService.findByUserId(sysUser.getId());
List<SimpleGrantedAuthority> authorityList = new ArrayList<>();
if (!CollectionUtils.isEmpty(permissionList)) {
for (SysPermission sysPermission : permissionList) {
authorityList.add(new SimpleGrantedAuthority(sysPermission.getCode()));
}
} MyUser myUser = new MyUser(sysUser.getUsername(), passwordEncoder.encode(sysUser.getPassword()), authorityList); log.info("登录成功!用户: {}", JSON.toJSONString(myUser)); return myUser;
}
}

4.8.  验证

当我们看到这个界面的时候,表示认证服务器配置完成  

5.  两个客户端

5.1.  Maven依赖

<?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 http://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.1.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.cjs.sso</groupId>
<artifactId>oauth2-sso-client-member</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>oauth2-sso-client-member</name>
<description>Demo project for Spring Boot</description> <properties>
<java.version>1.8</java.version>
</properties> <dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth.boot</groupId>
<artifactId>spring-security-oauth2-autoconfigure</artifactId>
<version>2.1.3.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>
<version>3.0.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency> <dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies> <build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build> </project>

5.2.  application.yml

server:
port: 8082
servlet:
context-path: /memberSystem
security:
oauth2:
client:
client-id: UserManagement
client-secret: user123
access-token-uri: http://localhost:8080/oauth/token
user-authorization-uri: http://localhost:8080/oauth/authorize
resource:
jwt:
key-uri: http://localhost:8080/oauth/token_key

这里context-path不要设成/,不然重定向获取code的时候回被拦截

5.3.  WebSecurityConfig

package com.cjs.example.config;

import com.cjs.example.util.EnvironmentUtils;
import org.springframework.beans.factory.annotation.Autowired;
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; /**
* @author ChengJianSheng
* @date 2019-03-03
*/
@EnableOAuth2Sso
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired
private EnvironmentUtils environmentUtils; @Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/bootstrap/**");
} @Override
protected void configure(HttpSecurity http) throws Exception {
if ("local".equals(environmentUtils.getActiveProfile())) {
http.authorizeRequests().anyRequest().permitAll();
}else {
http.logout().logoutSuccessUrl("http://localhost:8080/logout")
.and()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.csrf().disable();
}
}
}

说明:

  1. 最重要的注解是@EnableOAuth2Sso
  2. 权限控制这里采用Spring Security方法级别的访问控制,结合Thymeleaf可以很容易做权限控制
  3. 顺便多提一句,如果是前后端分离的话,前端需求加载用户的权限,然后判断应该显示那些按钮那些菜单

5.4.  MemberController

package com.cjs.example.controller;

import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody; import java.security.Principal; /**
* @author ChengJianSheng
* @date 2019-03-03
*/
@Controller
@RequestMapping("/member")
public class MemberController { @GetMapping("/list")
public String list() { return "member/list";
} @GetMapping("/info")
@ResponseBody
public Principal info(Principal principal) {
return principal;
} @GetMapping("/me")
@ResponseBody
public Authentication me(Authentication authentication) {
return authentication;
} @PreAuthorize("hasAuthority('member:save')")
@ResponseBody
@PostMapping("/add")
public String add() { return "add";
} @PreAuthorize("hasAuthority('member:detail')")
@ResponseBody
@GetMapping("/detail")
public String detail() {
return "detail";
}
}

5.5. Order项目跟它是一样的

server:
port: 8083
servlet:
context-path: /orderSystem
security:
oauth2:
client:
client-id: OrderManagement
client-secret: order123
access-token-uri: http://localhost:8080/oauth/token
user-authorization-uri: http://localhost:8080/oauth/authorize
resource:
jwt:
key-uri: http://localhost:8080/oauth/token_key

5.6.  关于退出

退出就是清空用于与SSO客户端建立的所有的会话,简单的来说就是使所有端点的Session失效,如果想做得更好的话可以令Token失效,但是由于我们用的JWT,故而撤销Token就不是那么容易,关于这一点,在官网上也有提到:

本例中采用的方式是在退出的时候先退出业务服务器,成功以后再回调认证服务器,但是这样有一个问题,就是需要主动依次调用各个业务服务器的logout

6.  工程结构

附上源码:  https://github.com/chengjiansheng/cjs-oauth2-sso-demo.git

7.  演示

8.  参考

https://www.cnblogs.com/cjsblog/p/9174797.html

https://www.cnblogs.com/cjsblog/p/9184173.html

https://www.cnblogs.com/cjsblog/p/9230990.html

https://www.cnblogs.com/cjsblog/p/9277677.html

https://blog.csdn.net/fooelliot/article/details/83617941

http://blog.leapoahead.com/2015/09/07/user-authentication-with-jwt/

https://www.cnblogs.com/lihaoyang/p/8581077.html

https://www.cnblogs.com/charlypage/p/9383420.html

http://www.360doc.com/content/18/0306/17/16915_734789216.shtml

https://blog.csdn.net/chenjianandiyi/article/details/78604376

https://www.baeldung.com/spring-security-oauth-jwt

https://www.baeldung.com/spring-security-oauth-revoke-tokens

https://www.reinforce.cn/t/630.html

9.  文档

https://projects.spring.io/spring-security-oauth/docs/oauth2.html

https://docs.spring.io/spring-security-oauth2-boot/docs/2.1.3.RELEASE/reference/htmlsingle/

https://docs.spring.io/spring-security-oauth2-boot/docs/2.1.3.RELEASE/

https://docs.spring.io/spring-security-oauth2-boot/docs/

https://docs.spring.io/spring-boot/docs/2.1.3.RELEASE/

https://docs.spring.io/spring-boot/docs/

https://docs.spring.io/spring-framework/docs/

https://docs.spring.io/spring-framework/docs/5.1.4.RELEASE/

https://spring.io/guides/tutorials/spring-boot-oauth2/

https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#core-services-password-encoding

https://spring.io/projects/spring-cloud-security

https://cloud.spring.io/spring-cloud-security/single/spring-cloud-security.html

https://docs.spring.io/spring-session/docs/current/reference/html5/guides/java-security.html

https://docs.spring.io/spring-session/docs/current/reference/html5/guides/boot-redis.html#boot-spring-configuration

OAuth2实现单点登录SSO的更多相关文章

  1. SpringCloud微服务实战——搭建企业级开发框架(四十):使用Spring Security OAuth2实现单点登录(SSO)系统

    一.单点登录SSO介绍   目前每家企业或者平台都存在不止一套系统,由于历史原因每套系统采购于不同厂商,所以系统间都是相互独立的,都有自己的用户鉴权认证体系,当用户进行登录系统时,不得不记住每套系统的 ...

  2. cas单点登录 SSO 的实现原理

    原文出处: cutesource   欢迎分享原创到伯乐头条 单点登录SSO(Single Sign On)说得简单点就是在一个多系统共存的环境下,用户在一处登录后,就不用在其他系统中登录,也就是用户 ...

  3. 单点登录 SSO 的实现原理

    单点登录SSO(Single Sign On)说得简单点就是在一个多系统共存的环境下,用户在一处登录后,就不用在其他系统中登录,也就是用户的一次登录能得到其他所有系统的信任. 单点登录在大型网站里使用 ...

  4. Atitit. 单点登录sso 的解决方案 总结

    Atitit.  单点登录sso 的解决方案 总结 1. 系统应用场景and SSO模式选型 2 2. 系统应用的原则与要求 2 2.1. 开发快速简单::绝大部分系统来说,开发快速简单为主 2 2. ...

  5. 十六、【适合中小企业的.Net轻量级开源框架】EnterpriseFrameWork框架核心类库之单点登录SSO

    回<[开源]EnterpriseFrameWork框架系列文章索引> EFW框架源代码下载:http://pan.baidu.com/s/1qWJjo3U 单点登录(Single Sign ...

  6. 单点登录SSO的实现原理

    单点登录SSO(Single Sign On)说得简单点就是在一个多系统共存的环境下,用户在一处登录后,就不用在其他系统中登录,也就是用户的一次登录能得到其他所有系统的信任.单点登录在大型网站里使用得 ...

  7. 单点登录SSO原理

    最近接触了一点单点登录的知识,有一点理解,记录一下.有些问题并没有找到完美的解决方法,还需要找点已有框架来看看. 欢迎留言探讨. 1       概念 1.1     概念及理解 有一个网上广为流传的 ...

  8. CAS单点登录(SSO)完整教程

    转:http://blog.csdn.net/frinder/article/details/7969925 CAS单点登录(SSO)完整教程(2012-02-01更新) 一.教程说明 前言 教程目的 ...

  9. Redis缓存实现单点登录SSO

    .NET基于Redis缓存实现单点登录SSO的解决方案 .NET基于Redis缓存实现单点登录SSO的解决方案   一.基本概念 最近公司的多个业务系统要统一整合使用同一个登录,这就是我们耳熟能详的单 ...

随机推荐

  1. Go语言Context(设计及分析)

    context简单概述: Go服务器的每个请求都有自己的goroutine,而有的请求为了提高性能,会经常启动额外的goroutine处理请求,当该请求被取消或超时,该请求上的所有goroutines ...

  2. C++中“wchar_t* ”和“ char * ”之间的相互转换

    把char*转换为wchar_t* 用stdlib.h中的mbstowcs_s函数,可以通过下面的例子了解其用法: char *CStr = "string to convert" ...

  3. BZOJ_3879_SvT_后缀数组+单调栈

    BZOJ_3879_SvT_后缀数组+单调栈 Description (我并不想告诉你题目名字是什么鬼) 有一个长度为n的仅包含小写字母的字符串S,下标范围为[1,n]. 现在有若干组询问,对于每一个 ...

  4. BZOJ_3932_[CQOI2015]任务查询系统_主席树

    BZOJ_3932_[CQOI2015]任务查询系统_主席树 题意: 最近实验室正在为其管理的超级计算机编制一套任务管理系统,而你被安排完成其中的查询部分.超级计算机中的 任务用三元组(Si,Ei,P ...

  5. Dubbo原理和源码解析之服务引用

    一.框架设计 在官方<Dubbo 开发指南>框架设计部分,给出了引用服务时序图: 另外,在官方<Dubbo 用户指南>集群容错部分,给出了服务引用的各功能组件关系图: 本文将根 ...

  6. WebService-----Xfire

    Xfire相对Axis2 个人感觉相对简单一点,因为myEclipse上可以自动生成.而且在目前来讲,Xfire也是更受欢迎一点. 首先说明Xfire所用jar包:http://download.cs ...

  7. 列举Java中常用的包、类和接口

    常用的类: BufferedReader ,BufferedWriter FileReader    ,FileWirter String      ,Integer Date        ,Cla ...

  8. WebGL three.js学习笔记 使用粒子系统模拟时空隧道(虫洞)

    WebGL three.js学习笔记 使用粒子系统模拟时空隧道 本例的运行结果如图: 时空隧道demo演示 Demo地址:https://nsytsqdtn.github.io/demo/sprite ...

  9. PostCSS 基本用法

    1.postcss相关网站 https://www.postcss.com.cn/ https://www.ibm.com/developerworks/cn/web/1604-postcss-css ...

  10. 阿里开源分布式事务解决方案 Fescar

    微服务倡导将复杂的单体应用拆分为若干个功能简单.松耦合的服务,这样可以降低开发难度.增强扩展性.便于敏捷开发.当前被越来越多的开发者推崇,系统微服务化后,一个看似简单的功能,内部可能需要调用多个服务并 ...