Security OAuth2 SSO单点登录(一)

学习链接

Security OAuth2 SSO单点登录源码剖析ing…(二)

cookie & session & localStorage & sessionStorage - 自己的

session共享原理 - 自己的

spring-session学习 - 自己的

OAuth2流程和源码解析 - 自己的 - 有道笔记

Security OAuth2 授权 & JWT - 自己的

Spring Security整合Gitee第三方登录 - 自己的

JWT使用 - 自己的

token无感刷新【渡一教育】

单点登录的模式【渡一教育】

Spring Boot+OAuth2,一个注解搞定单点登录!

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

Spring Security Oauth2 SSO单点登录配置及原理深度剖析

多系统单点登录(sso)设计

Java-前后端分离-单点登录(SSO二级跨域和跨一级域名)

【学习笔记】:基于SpringBoot实现单点登录sso测试demo

springsecurity实现单点登录

最简单易懂的OSS单点登录设计与实现

主要是体会下,security是如何考虑单点登录的,以及它的具体做法:全部代码 - gitee

单点登录

cookie+session&token模式&前端无感token刷新&双token模式&黑名单

当1个公司开发了多个系统时,如系统A,系统B,系统C。用户在登录A系统时,需要跳转到B系统时,在B系统又要去登录一遍,这无疑时是比较麻烦的。

这时候,就可以抽离出1个统一认证中心服务专门处理用户认证。首先,当用户访问系统A时,由于用户没有登录,所以会跳转到统一认证中心登录,当用户登录成功后,在统一认证中心下可以使用cookie + session的机制保存用户的登录信息,然后用户跳转到系统A,并且携带登录统一认证中心成功的凭证(等等,这里面可能会涉及到cookie的不可跨域名性,就算存在localStorage里面也是有着隔离,这些是具体的细节,暂不考虑,实现时再说),这样系统A就收到了用户发过来的凭证,然而系统A却不能鉴定该凭证的真实性以及该凭证所代表的用户信息,因此系统A需要发送请求给认证中心获取该凭证所对应的用户数据(等等,如果认证服务与系统A在浏览器都会携带同1个cookie时,是可以用spring-session实现会话共享的,也就不用发请求了。补充:访问这2个服务能不能携带同1个cookie是有条件的)。系统A通过发请求给认证中心校验凭证有效时,就会在凭证允许的权限范围内给用户访问。此时,当用户访问系统B时,要仍然携带凭证(等等,此时跳转到系统B,这个凭证由于不可跨域名性,不一定能带上,可以先跳回认证中心,因为已经在认证中心登录过,再跳转到系统B,并携带凭证),系统B也同样需要发请求给认证中心,校验凭证的真实性和用户信息,如果没有问题,则允许用户访问。

上面实现的大致逻辑就是这样,里面存在1个问题:如果某个系统,比如系统C比较热门,用户比较多的情况下,比如:200万用户都活跃,这样认证中心就得统一管理这么多已登录用户的信息,并且每一次用户访问系统C都需要访问认证中心来获取用户信息。那么对于认证中心来说,1个是存储用户信息问题(这个可以用redis解决),1个是处理大量校验凭证请求的问题,这样就很有可能导致认证中心不堪重负,假设此时认证中心挂了,那么所有的子系统就全都报废了(这个就比较严重了,各个子系统都依赖这个认证中心才能正常工作)。但是,这样集中式的管理用户会话数据,会比较方便对用户进行控制,比如让用户下线只需要删除这个会话即可,那么用户会立即需要重新认证登录。

对于上面的问题,使用jwt令牌可以解决,当用户在统一认证中心登录成功时,生成1个jwt令牌,这个jwt令牌由3部分组成:1个令牌类型和算法,1个令牌内容可base64解码,1个签名可验签。用户登录成功后,保存此令牌。然后,拿着这个令牌去访问系统A,因为jwt令牌上已经携带的内容包含了用户信息,所以系统A可以直接读取这个jwt令牌中的用户信息,并且可以验证签名来判断jwt令牌的内容是否被篡改过,如果没有被篡改过,并且这个jwt令牌的失效时间还长,并没有过期。那么系统A就直接给用户返回内容即可。此时,用户访问系统C也是一样携带这个令牌。(至于跳转系统C的时候,是怎么携带这个令牌,具体实现的时候再说,反正就是这么个意思)

上面使用jwt的token方式就是这样,认证中心不再保存已登录用户的信息,而是给了登录用户1个可识别和可验签的代表用户信息的令牌。认证中心也不再处理校验凭证的请求了。即便认证中心挂掉了,此令牌仍然可在各子系统中使用。这样也会带来一些问题:不能对用户进行方便控制,不好做到让用户立即下线。令牌一旦颁发出去,那么想撤销这个令牌就相对来说没那么容易了。虽然,也可以采用黑名单的方式,每个子系统在redis中维护着自己黑名单(各个子系统不一定使用的是同一个redis),但撤销还得通知到其它系统。

jwt令牌减轻了认证中心的压力,但带来了撤销令牌的麻烦。因此,OAuth2中就涉及到了1个刷新令牌的机制。既然令牌不能立即撤销,那么给这个令牌1个比较短的过期时间,比如:2个小时。也就是,用户登录后,会获得2个令牌,1个访问令牌,1个刷新令牌。用户使用访问令牌访问各个子系统,但因为访问令牌是有时间限制的,一旦到了过期时间,访问令牌就被各个子系统识别出来已经过期了,无法使用。此时,再让用户登录一遍的话,就也显得麻烦了,因此就让用户需要拿着刷新令牌(这个刷新令牌的有效时间比访问令牌的时间要长,比如:7天或1个月,)去认证中心换取新的访问令牌,然后拿着这个新的访问令牌去访问各个子系统。这样,用户每隔一段比较短的时间,就得拿着刷新令牌获取访问令牌。如果此时要控制用户的话,等到用户下一次获取访问令牌时,不给他刷新就行了,那么用户就只得重新登录了,只是没有那么即时了。

如果需要即时性,那么就由认证服务给各子系统通知一下(这个通知接口也得做权限控制),更新各子系统存在redis中的黑名单,黑名单记录用户的id(因为令牌颁发后,不会记录该令牌具体的值,否则跟在会话中记录用户用户数据没啥区别了,同时也是内存负担)存储个2小时,那么这2个小时内,用户使用旧的访问令牌是无法访问的,当用户使用刷新令牌获取新的访问令牌时,这时候就可以决定是否颁发新的令牌,如果颁发新的访问令牌,则通知各个子系统从黑名单中主动移除。

还有1个比较严重的问题需要考虑下,如果黑名单只是记录用户的id的话,可能还是不够。假设用户登录了1次之后,获取了访问令牌和刷新令牌,持有访问令牌在令牌有效期内可以任意访问用户资源,持有刷新令牌同样在有效期内可重新获取刷新令牌。那么如果用户修改了密码的情况下,它们在有效期内仍然能够使用,令牌的重要性甚至上升到了与用户密码凭据同样的高度。同样,用户在多个设备而登录多次时,那么就会存在1个用户有多个访问令牌和多个刷新令牌的存在。因此,登录成功后,最好是将用户id和对应的访问令牌和刷新令牌的JTI属性(令牌唯一标识)关系也给保存下来。当用户修改密码时,找到所有之前的访问令牌id,把它们通知给各个子系统,让它们写到黑名单中,找到所有之前的刷新令牌id,然后把他们写到认证服务中心的黑名单中,并且也可以做到最多允许同时在线客户端数量,根据设置确定踢人模式。而一旦用户修改密码,则将之前颁发给该用户的所有访问令牌的JTI标识写到所有子系统,同时,把用户对应的刷新令牌写到认证中心黑名单。

auth-server

引入依赖

<?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 https://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.2.6.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    
    <groupId>org.javaboy</groupId>
    <artifactId>auth-server</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    
    <name>auth-server</name>
    
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Hoxton.SR3</spring-cloud.version>
    </properties>

    <dependencies>
    
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-oauth2</artifactId>
        </dependency>
        
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-security</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

application.yml

server:
  port: 1110

AuthServerConfig

@Configuration
@EnableAuthorizationServer
public class AuthServerConfig extends AuthorizationServerConfigurerAdapter {
    @Autowired
    PasswordEncoder passwordEncoder;

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security.checkTokenAccess("permitAll()");
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                .withClient("c1")
                .secret(passwordEncoder.encode("secret"))
                .autoApprove(true)
                .redirectUris("http://localhost:1111/login", "http://localhost:1112/login")
                .scopes("user")
                .accessTokenValiditySeconds(7200)
                .authorizedGrantTypes("authorization_code");

    }
}

SecurityConfig

@Configuration
@Order(1)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Bean
    PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/login.html", "/css/**", "/js/**", "/images/**", "/favicon.ico");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.requestMatchers()
                .antMatchers("/login")
                .antMatchers("/oauth/authorize")
                .and()
                .authorizeRequests().anyRequest().authenticated()
                .and()
                .formLogin()
                .loginPage("/login.html")
                .loginProcessingUrl("/login")
                .permitAll()
                .and()
                .csrf().disable();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                .withUser("zzhua")
                .password(passwordEncoder().encode("zzhua"))
                .roles("admin");
    }
}

UserController

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

login.html

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
<div style="overflow: hidden">
    <div class="login-form">
        <h1>统一认证平台</h1>
        <form action="/login" method="post">
            <div class="login-item">
                <span class="login-item-name">用户名: </span>
                <input type="text" name="username"/>
            </div>
            <div class="login-item">
                <span class="login-item-name">密码: </span>
                <input type="text" name="password"/>
            </div>
            <div class="login-item submit">
                <input type="submit" value="登录"/>
            </div>
        </form>

    </div>
</div>
</body>
<style>
    * {
        box-sizing: border-box;
    }
    body {
        overflow: hidden;
        margin: 0;
    }
    h1 {
        margin-left: 25px;
    }
    .login-form {
        width: 600px;
        margin: 120px auto;
        text-align: center;
        border: 1px solid #eee;
        border-radius: 5px;
        box-shadow: 2px 9px 6px 0px #ddd;
    }

    .login-form form {
        width: 60%;
        margin: 20px auto;
        padding: 10px 0 50px;
    }

    .login-item {
        display: flex;
        margin-bottom: 20px;
        height: 26px;
        line-height: 32px;
    }
    .login-item .login-item-name {
        width: 100px;
        text-align: right;
        padding-right: 15px;
    }

    .login-item input {
        flex: 1;
        outline: none;
        border: 1px solid #ddd;
        height: 32px;
        font-size: 18px;
        font-weight: lighter;
        padding-left: 10px;
        border-radius: 5px;
    }

    .login-item.submit {
        padding-left: 100px;
    }

    .login-item.submit input[type=submit]{
        border: none;
        background-color: #86bdcf;
        letter-spacing: 10px;
        color: #ffffff;
        transition: all .2s;
    }

    .login-item.submit input[type=submit]:hover {
        background-color: #6da0b2;
    }

</style>
</html>

client1

引入依赖

<?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 https://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.2.6.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    
    <groupId>org.javaboy</groupId>
    <artifactId>client1</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    
    <name>client1</name>
    
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Hoxton.SR3</spring-cloud.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-oauth2</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-security</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

application.yml配置文件

server:
  port: 1111
  servlet:
    session:
      cookie:
        name: s1
security:
  oauth2:
    client:
      client-id: c1
      client-secret: secret
      user-authorization-uri: http://localhost:1110/oauth/authorize
      access-token-uri: http://localhost:1110/oauth/token
    resource:
      user-info-uri: http://localhost:1110/user

SecurityConfig

@Configuration
@EnableOAuth2Sso
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().anyRequest().authenticated().and().csrf().disable();
    }
}

Client1Application

@SpringBootApplication
public class Client1Application {

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

}

HaloController

@RestController
public class HaloController {

    @GetMapping("/halo")
    public String halo() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        System.out.println(authentication.getName() + Arrays.toString(authentication.getAuthorities().toArray()));
        return "halo-1111";
    }
}

index.html

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>

	<h1>【client1】</h1>
	
	<a href="http://localhost:1112">跳转到client2</a>
	
	<button id="btn1">获取client1中的数据</button>
	<button id="btn2">获取client2中的数据</button>
	
</body>
<script>

    let btn1 = document.querySelector("#btn1")
    let btn2 = document.querySelector("#btn2")
    
    btn1.addEventListener('click', function () {
        fetch("http://localhost:1111/halo")
    })
    
    btn2.addEventListener('click', function () {
        fetch("http://localhost:1112/halo")
    })
</script>
</html>

client2

引入依赖

<?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 https://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.2.6.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>org.javaboy</groupId>
    <artifactId>client2</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>client2</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Hoxton.SR3</spring-cloud.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-oauth2</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-security</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>


application.yml配置文件

server:
  port: 1112
  servlet:
    session:
      cookie:
        name: s2
security:
  oauth2:
    client:
      client-id: c1
      client-secret: secret
      user-authorization-uri: http://localhost:1110/oauth/authorize
      access-token-uri: http://localhost:1110/oauth/token
    resource:
      user-info-uri: http://localhost:1110/user


SecurityConfig

@Configuration
@EnableOAuth2Sso
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().anyRequest().authenticated().and().csrf().disable();
    }
}

Client2Application

@SpringBootApplication
public class Client2Application {

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

}

HaloController

@RestController
public class HaloController {
    @CrossOrigin
    @GetMapping("/halo")
    public String halo() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        System.out.println(authentication.getName() + Arrays.toString(authentication.getAuthorities().toArray()));
        return "halo";
    }
}

index.html

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>

	<h1>【client2】</h1>
	
	<a href="http://localhost:1111">跳转到client1</a>
	
	<button id="btn1">获取client1中的数据</button>
	<button id="btn2">获取client2中的数据</button>

</body>
<script>

    let btn1 = document.querySelector("#btn1")
    let btn2 = document.querySelector("#btn2")
    
    btn1.addEventListener('click', function () {
        fetch("http://localhost:1111/halo")
    })
    
    btn2.addEventListener('click', function () {
        fetch("http://localhost:1112/halo")
    })
</script>
</html>

  • 14
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
该资源内项目源码是个人的课程设计、毕业设计,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到96分,放心下载使用! ## 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。 该资源内项目源码是个人的课程设计,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到96分,放心下载使用! ## 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。
Spring Security OAuth2可以实现单点登录功能。通过引入相关的jar包,可以在Spring Boot项目中使用注解来实现单点登录客户端的功能。具体步骤如下: 1. 创建一个单点登录客户端工程,并引入以下依赖: ``` <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> </dependency> ``` 2. 在项目中使用注解来配置单点登录客户端的相关信息,例如授权服务器的URL、客户端ID和密钥等。 需要注意的是,根据Spring Security官方的最新推荐,Spring Security OAuth2项目已经不再推荐使用,而是将OAuth2的相关功能抽取出来,集成在Spring Security中,并单独新建了spring-authorization-server项目来实现授权服务器的功能。因此,如果需要实现授权服务器的功能,可以使用spring-authorization-server项目。 总结来说,Spring Security OAuth2可以实现单点登录功能,但是根据最新的推荐,建议使用Spring Security和spring-authorization-server来实现授权服务器的功能。\[1\]\[2\] #### 引用[.reference_title] - *1* *2* [SpringCloud微服务实战——搭建企业级开发框架(四十):使用Spring Security OAuth2实现单点登录(SSO)系统](https://blog.csdn.net/wmz1932/article/details/124719588)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [Spring Security OAuth2 单点登录](https://blog.csdn.net/weixin_42073629/article/details/115436378)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值