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/permissionusername: rootpassword: 123456driver-class-name: com.mysql.jdbc.Driverjpa:show-sql: truesession:store-type: redisredis:host: 127.0.0.1password: 123456port: 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 {@Autowiredprivate DataSource dataSource;@Overridepublic void configure(AuthorizationServerSecurityConfigurer security) throws Exception {security.allowFormAuthenticationForClients();security.tokenKeyAccess("isAuthenticated()");}@Overridepublic void configure(ClientDetailsServiceConfigurer clients) throws Exception {clients.jdbc(dataSource);}@Overridepublic void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {endpoints.accessTokenConverter(jwtAccessTokenConverter());endpoints.tokenStore(jwtTokenStore());
//        endpoints.tokenServices(defaultTokenServices());}/*@Primary@Beanpublic DefaultTokenServices defaultTokenServices() {DefaultTokenServices defaultTokenServices = new DefaultTokenServices();defaultTokenServices.setTokenStore(jwtTokenStore());defaultTokenServices.setSupportRefreshToken(true);return defaultTokenServices;}*/@Beanpublic JwtTokenStore jwtTokenStore() {return new JwtTokenStore(jwtAccessTokenConverter());}@Beanpublic JwtAccessTokenConverter jwtAccessTokenConverter() {JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();jwtAccessTokenConverter.setSigningKey("cjs");   //  Sets the JWT signing keyreturn 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 {@Autowiredprivate MyUserDetailsService userDetailsService;@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());}@Overridepublic void configure(WebSecurity web) throws Exception {web.ignoring().antMatchers("/assets/**", "/css/**", "/images/**");}@Overrideprotected void configure(HttpSecurity http) throws Exception {http.formLogin().loginPage("/login").and().authorizeRequests().antMatchers("/login").permitAll().anyRequest().authenticated().and().csrf().disable().cors();}@Beanpublic 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;   //  举个例子,部门IDprivate 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 {@Autowiredprivate PasswordEncoder passwordEncoder;@Autowiredprivate UserService userService;@Autowiredprivate PermissionService permissionService;@Overridepublic 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: 8082servlet:context-path: /memberSystem
security:oauth2:client:client-id: UserManagementclient-secret: user123access-token-uri: http://localhost:8080/oauth/tokenuser-authorization-uri: http://localhost:8080/oauth/authorizeresource: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 {@Autowiredprivate EnvironmentUtils environmentUtils;@Overridepublic void configure(WebSecurity web) throws Exception {web.ignoring().antMatchers("/bootstrap/**");}@Overrideprotected 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")@ResponseBodypublic Principal info(Principal principal) {return principal;}@GetMapping("/me")@ResponseBodypublic 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: 8083servlet:context-path: /orderSystem
security:oauth2:client:client-id: OrderManagementclient-secret: order123access-token-uri: http://localhost:8080/oauth/tokenuser-authorization-uri: http://localhost:8080/oauth/authorizeresource: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

作者:废物大师兄

cnblogs.com/cjsblog/p/10548022.html

若觉得文章对你有帮助,随手转发分享,也是侠梦继续更新的动力。

(附源码)Spring Boot 框架整合 OAuth2 实现单点登录 SSO 详细完整源码教程!相关推荐

  1. spring boot 学习(二)spring boot 框架整合 thymeleaf

    spring boot 框架整合 thymeleaf spring boot 的官方文档中建议开发者使用模板引擎,避免使用 JSP.因为若一定要使用 JSP 将无法使用. 注意:本文主要参考学习了大神 ...

  2. OAuth2 实现单点登录 SSO

    转载自  OAuth2 实现单点登录 SSO 1. 前言 技术这东西吧,看别人写的好像很简单似的,到自己去写的时候就各种问题,"一看就会,一做就错".网上关于实现SSO的文章一大堆 ...

  3. OAuth2实现单点登录SSO

    本文转载自:https://www.cnblogs.com/cjsblog/p/10548022.html OAuth2实现单点登录SSO 1.  前言 技术这东西吧,看别人写的好像很简单似的,到自己 ...

  4. 使用Spring Security OAuth2实现单点登录(SSO)系统

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

  5. OAuth2实现单点登录SSO完整教程,其实不难!

    我是猿人,一个热爱技术.热爱编程的IT猿.技术是开源的,知识是共享的! 写作是对自己学习的总结和记录,如果您对 Java.分布式.微服务.中间件.Spring Boot.Spring Cloud等技术 ...

  6. Spring Boot Security 整合 OAuth2 设计安全API接口服务

    OAuth2概述 oauth2根据使用场景不同,分成了4种模式 授权码模式(authorization code) 简化模式(implicit) 密码模式(resource owner passwor ...

  7. Spring Security 4 整合Hibernate 实现持久化登录验证(带源码)

    上一篇文章:Spring Security 4 整合Hibernate Bcrypt密码加密(带源码) 原文地址:http://websystique.com/spring-security/spri ...

  8. Spring Cloud Security:Oauth2实现单点登录

    摘要 Spring Cloud Security 为构建安全的SpringBoot应用提供了一系列解决方案,结合Oauth2可以实现单点登录功能,本文将对其单点登录用法进行详细介绍. 单点登录简介 单 ...

  9. (附源码)spring boot宠物医院管理系统 毕业设计180923

    Springboot宠物医院管理系统 摘 要 现如今生活质量提高,人们追求精神健康,与家中宠物朝夕相处,感情深厚,宠物渐渐成了我们身边的朋友.因而宠物生病了,需要去看病,自古医院救死扶伤,生命无贵贱, ...

最新文章

  1. 策略模式 极其简单的列子
  2. 【EventBus】事件通信框架 ( 总结 | 手写事件通信框架完整代码示例 | 测试上述框架 )
  3. IPSEC ×××实验六:ASA SSL ×××
  4. Android NFC开发
  5. SQL中Group By的用法整理
  6. nssl1191,P2700-逐个击破(平津战役)【并查集】
  7. C语言的atoi和C++的to_string
  8. jQuery DOM 节点操作
  9. system volume information是什么文件_如何扩展Unity URP的后处理Volume组件
  10. jeecg中的树形控件demo
  11. 1971旗舰cpu intel_Intel 酷睿i7 9700F搭配影驰GeForce RTX 2060大将
  12. 【题解】Making The Grade(DP+结论)
  13. 光配件招标采购技术标准
  14. Viterbi算法(维特比算法)
  15. plc应用与c语言编程区别,PLC编程与应用入门基础知识
  16. html5实现统计功能,基于HTML5的统计图表系统的设计与实现
  17. 分时线的9代表什么_股权生命9条线,持有不同股权代表了什么?
  18. 如何将陈述句变为疑问句
  19. 【Scala-spark.mlib】分布式矩阵——分布式计算的基石与利器
  20. 视频编辑,如何截取视频片段

热门文章

  1. Git命令: rebase 和 squash命令
  2. 工程师必须了解的IQ信号和调制知识
  3. 密码打码就安全了吗?这款星标 20K 的去 『马赛克』 开源神器,用 AI 一秒还原高清原图!...
  4. 全球及中国镱 (Yb) 蒸发材料行业深度研究及未来发展趋势调研报告2021年版
  5. 贪心算法1——找零钱问题
  6. Jmockit 静态方法mock
  7. MCS-51实现静态数码管的显示
  8. vue中不同模块间的跳转
  9. 互联网公司招聘解读!
  10. 还在if-else行走天下?试试【策略模式】吧