Spring Security Oauth2 单点登录案例实现和执行流程剖析

本文详细介绍了如何使用Spring Security OAuth2实现单点登录功能,包括项目结构、配置、安全配置和执行流程。案例中,认证服务器和多个客户端通过OAuth2授权码模式实现SSO。登录流程涉及OAuth2授权码模式的完整步骤,从客户端请求到资源服务器获取用户信息,再到登录认证的过程。
摘要由CSDN通过智能技术生成

Spring Security Oauth2
OAuth是一个关于授权的开放网络标准,在全世界得到的广泛的应用,目前是2.0的版本。OAuth2在“客户端”与“服务提供商”之间,设置了一个授权层(authorization layer)。“客户端”不能直接登录“服务提供商”,只能登录授权层,以此将用户与客户端分离。“客户端”登录需要获取OAuth提供的令牌,否则将提示认证失败而导致客户端无法访问服务。关于OAuth2这里就不多作介绍了,网上资料详尽。下面我们实现一个 整合 SpringBoot 、Spring Security OAuth2 来实现单点登录功能的案例并对执行流程进行详细的剖析。

案例实现
项目介绍
这个单点登录系统包括下面几个模块:

spring-oauth-parent : 父模块,管理打包

spring-oauth-server : 认证服务端、资源服务端(端口:8881)

spring-oauth-client : 单点登录客户端示例(端口:8882)

spring-oauth-client2: 单点登录客户端示例(端口:8883)

当通过任意客户端访问资源服务器受保护的接口时,会跳转到认证服务器的统一登录界面,要求登录,登录之后,在登录有效时间内任意客户端都无需再登录。

认证服务端
添加依赖

主要是添加 spring-security-oauth2 依赖。

pom.xml

复制代码

4.0.0

<artifactId>spring-oauth-server</artifactId>
<name>spring-oauth-server</name>
<packaging>war</packaging>

<parent>
    <groupId>com.louis</groupId>
    <artifactId>spring-oauth-parent</artifactId>
    <version>1.0.0-SNAPSHOT</version>
</parent>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.security.oauth</groupId>
        <artifactId>spring-security-oauth2</artifactId>
        <version>${oauth.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-freemarker</artifactId>
    </dependency>
</dependencies>
复制代码 配置文件

配置文件内容如下。

application.yml

server:
port: 8881
servlet:
context-path: /auth

启动类

启动类添加 @EnableResourceServer 注解,表示作为资源服务器。

OAuthServerApplication.java

复制代码
package com.louis.spring.oauth.server;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;

@SpringBootApplication
@EnableResourceServer
public class OAuthServerApplication extends SpringBootServletInitializer {

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

}
复制代码
认证服务配置

添加认证服务器配置,这里采用内存方式获取,其他方式获取在这里定制即可。

OAuthServerConfig.java

复制代码
package com.louis.spring.oauth.server.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
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.AuthorizationServerSecurityConfigurer;

@Configuration
@EnableAuthorizationServer
public class OAuthServerConfig extends AuthorizationServerConfigurerAdapter {

@Autowired    
private BCryptPasswordEncoder passwordEncoder;

@Override
public void configure(final AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
    oauthServer.tokenKeyAccess("permitAll()").checkTokenAccess("isAuthenticated()");
}

@Override
public void configure(final ClientDetailsServiceConfigurer clients) throws Exception {
    clients.inMemory()
        .withClient("SampleClientId") // clientId, 可以类比为用户名
        .secret(passwordEncoder.encode("secret")) // secret, 可以类比为密码
        .authorizedGrantTypes("authorization_code")    // 授权类型,这里选择授权码
        .scopes("user_info") // 授权范围
        .autoApprove(true) // 自动认证
        .redirectUris("http://localhost:8882/login","http://localhost:8883/login")    // 认证成功重定向URL
        .accessTokenValiditySeconds(10); // 超时时间,10s 
}

}
复制代码
安全配置

Spring Security 安全配置。在安全配置类里我们配置了:

  1. 配置请求URL的访问策略。

  2. 自定义了同一认证登录页面URL。

  3. 配置用户名密码信息从内存中创建并获取。

SecurityConfig.java

复制代码
package com.louis.spring.oauth.server.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
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.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

@Configuration
@Order(1)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.requestMatchers()
        .antMatchers("/login")
        .antMatchers("/oauth/authorize")
        .and()
        .authorizeRequests()
        .anyRequest().authenticated()
        .and()
        .formLogin().loginPage("/login").permitAll()    // 自定义登录页面,这里配置了 loginPage, 就会通过 LoginController 的 login 接口加载登录页面
        .and().csrf().disable();
    
}

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    // 配置用户名密码,这里采用内存方式,生产环境需要从数据库获取
    auth.inMemoryAuthentication()
        .withUser("admin")
        .password(passwordEncoder().encode("123"))
        .roles("USER");
}

@Bean
public BCryptPasswordEncoder passwordEncoder(){
    return new BCryptPasswordEncoder();
}

}
复制代码
接口提供

这里提供了一个自定义的登录接口,用于跳转到自定义的同一认证登录页面。

LoginController.java

复制代码
package com.louis.spring.oauth.server.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class LoginController {

/**
 * 自定义登录页面
 * @return
 */
@GetMapping("/login")
public String login() {
    return "login";
}

}
复制代码
登录页面放置在 resources/templates 下,需要在登录时提交 pos t表单到 auth/login。

login.ftl

复制代码

Insert title here 复制代码 这里提供了一个受保护的接口,用于获取用户信息,客户端访问这个接口的时候要求登录认证。

UserController.java

复制代码
package com.louis.spring.oauth.server.controller;

import java.security.Principal;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class UserController {

/**
 * 资源服务器提供的受保护接口
 * @param principal
 * @return
 */
@RequestMapping("/user")
public Principal user(Principal principal) {
    System.out.println(principal);
    return principal;
}

}
复制代码
客户端实现
添加依赖

主要添加 Spring Security 依赖,另外因为 Spring Boot 2.0 之后代码的合并, 需要添加 spring-security-oauth2-autoconfigure ,才能使用 @EnableOAuth2Sso 注解。

pom.xml

复制代码

4.0.0

<artifactId>spring-oauth-client</artifactId>
<name>spring-oauth-client</name>
<packaging>war</packaging>

<parent>
    <groupId>com.louis</groupId>
    <artifactId>spring-oauth-parent</artifactId>
    <version>1.0.0-SNAPSHOT</version>
</parent>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</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>${oauth-auto.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>
    <dependency>
        <groupId>org.thymeleaf.extras</groupId>
        <artifactId>thymeleaf-extras-springsecurity4</artifactId>
    </dependency>
</dependencies>
复制代码 启动类

启动类需要添加 RequestContextListener,用于监听HTTP请求事件。

OAuthClientApplication.java

复制代码
package com.louis.spring.oauth.client;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.context.annotation.Bean;
import org.springframework.web.context.request.RequestContextListener;

@SpringBootApplication
public class OAuthClientApplication extends SpringBootServletInitializer {

@Bean
public RequestContextListener requestContextListener() {
    return new RequestContextListener();
}

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

}
复制代码
安全

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值