Spring Cloud入门-Oauth2授权之基于JWT完成单点登录(Hoxton版本)

本文介绍SpringCloudSecurity结合Oauth2实现单点登录的方法,包括创建oauth2-client模块、配置授权服务器、演示网页和接口单点登录过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

项目使用的Spring Cloud为Hoxton版本,Spring Boot为2.2.2.RELEASE版本

Spring Cloud入门系列汇总

序号内容链接地址
1Spring Cloud入门-十分钟了解Spring Cloudhttps://blog.youkuaiyun.com/ThinkWon/article/details/103715146
2Spring Cloud入门-Eureka服务注册与发现(Hoxton版本)https://blog.youkuaiyun.com/ThinkWon/article/details/103726655
3Spring Cloud入门-Ribbon服务消费者(Hoxton版本)https://blog.youkuaiyun.com/ThinkWon/article/details/103729080
4Spring Cloud入门-Hystrix断路器(Hoxton版本)https://blog.youkuaiyun.com/ThinkWon/article/details/103732497
5Spring Cloud入门-Hystrix Dashboard与Turbine断路器监控(Hoxton版本)https://blog.youkuaiyun.com/ThinkWon/article/details/103734664
6Spring Cloud入门-OpenFeign服务消费者(Hoxton版本)https://blog.youkuaiyun.com/ThinkWon/article/details/103735751
7Spring Cloud入门-Zuul服务网关(Hoxton版本)https://blog.youkuaiyun.com/ThinkWon/article/details/103738851
8Spring Cloud入门-Config分布式配置中心(Hoxton版本)https://blog.youkuaiyun.com/ThinkWon/article/details/103739628
9Spring Cloud入门-Bus消息总线(Hoxton版本)https://blog.youkuaiyun.com/ThinkWon/article/details/103753372
10Spring Cloud入门-Sleuth服务链路跟踪(Hoxton版本)https://blog.youkuaiyun.com/ThinkWon/article/details/103753896
11Spring Cloud入门-Consul服务注册发现与配置中心(Hoxton版本)https://blog.youkuaiyun.com/ThinkWon/article/details/103756139
12Spring Cloud入门-Gateway服务网关(Hoxton版本)https://blog.youkuaiyun.com/ThinkWon/article/details/103757927
13Spring Cloud入门-Admin服务监控中心(Hoxton版本)https://blog.youkuaiyun.com/ThinkWon/article/details/103758697
14Spring Cloud入门-Oauth2授权的使用(Hoxton版本)https://blog.youkuaiyun.com/ThinkWon/article/details/103761687
15Spring Cloud入门-Oauth2授权之JWT集成(Hoxton版本)https://blog.youkuaiyun.com/ThinkWon/article/details/103763364
16Spring Cloud入门-Oauth2授权之基于JWT完成单点登录(Hoxton版本)https://blog.youkuaiyun.com/ThinkWon/article/details/103766368
17Spring Cloud入门-Nacos实现注册和配置中心(Hoxton版本)https://blog.youkuaiyun.com/ThinkWon/article/details/103769680
18Spring Cloud入门-Sentinel实现服务限流、熔断与降级(Hoxton版本)https://blog.youkuaiyun.com/ThinkWon/article/details/103770879
19Spring Cloud入门-Seata处理分布式事务问题(Hoxton版本)https://blog.youkuaiyun.com/ThinkWon/article/details/103786102
20Spring Cloud入门-汇总篇(Hoxton版本)https://blog.youkuaiyun.com/ThinkWon/article/details/103786588

摘要

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

单点登录简介

单点登录(Single Sign On)指的是当有多个系统需要登录时,用户只需登录一个系统,就可以访问其他需要登录的系统而无需登录。

创建oauth2-client模块

这里我们创建一个oauth2-client服务作为需要登录的客户端服务,使用上一节中的oauth2-jwt-server服务作为授权服务,当我们在oauth2-jwt-server服务上登录以后,就可以直接访问oauth2-client需要登录的接口,来演示下单点登录功能。

在pom.xml中添加相关依赖:

<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>

在application.yml中进行配置:

server:
  port: 9501
  servlet:
    session:
      cookie:
        # 防止cookie冲突,冲突会导致登录验证不通过
        name: OAUTH2-CLIENT-SESSIONID

oauth2-service-url: http://localhost:9401

spring:
  application:
    name: oauth2-client

security:
  # 与oauth2-server对应的配置
  oauth2:
    client:
      client-id: admin
      client-secret: admin123456
      user-authorization-uri: ${oauth2-service-url}/oauth/authorize
      access-token-uri: ${oauth2-service-url}/oauth/token
    resource:
      jwt:
        key-uri: ${oauth2-service-url}/oauth/token_key

在启动类上添加@EnableOAuth2Sso注解来启用单点登录功能:

@EnableOAuth2Sso
@SpringBootApplication
public class Oauth2ClientApplication {

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

}

添加接口用于获取当前登录用户信息:

@RestController
@RequestMapping("/user")
public class UserController {

    @GetMapping("/getCurrentUser")
    public Object getCurrentUser(Authentication authentication) {
        return authentication;
    }

}

修改授权服务器配置

修改oauth2-jwt-server模块中的AuthorizationServerConfig类,将绑定的跳转路径为http://localhost:9501/login,并添加获取秘钥时的身份认证。

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
    //以上省略一堆代码...
    
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                // 配置client_id
                .withClient("admin")
                // 配置client_secret
                .secret(passwordEncoder.encode("admin123456"))
                // 配置访问token的有效期
                .accessTokenValiditySeconds(3600)
                // 配置刷新token的有效期
                .refreshTokenValiditySeconds(864000)
                // 配置redirect_uri,用于授权成功后的跳转
                // .redirectUris("http://www.baidu.com")
                // 单点登录时配置
                .redirectUris("http://localhost:9501/login")
                // 自动授权配置
                // .autoApprove(true)
                // 配置申请的权限范围
                .scopes("all")
                // 配置grant_type,表示授权类型
                .authorizedGrantTypes("authorization_code", "password", "refresh_token");
    }

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        // 获取密钥需要身份认证,使用单点登录时必须配置
        security.tokenKeyAccess("isAuthenticated()");
    }
}

网页单点登录演示

启动oauth2-client服务和oauth2-jwt-server服务;

访问客户端需要授权的接口http://localhost:9501/user/getCurrentUser会跳转到授权服务的登录界面;

在这里插入图片描述

进行登录操作后跳转到授权页面;

在这里插入图片描述

授权后会跳转到原来需要权限的接口地址,展示登录用户信息;

在这里插入图片描述

如果需要跳过授权操作进行自动授权可以添加autoApprove(true)配置:

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
    //以上省略一堆代码...
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                // 配置client_id
                .withClient("admin")
                // 配置client_secret
                .secret(passwordEncoder.encode("admin123456"))
                // 配置访问token的有效期
                .accessTokenValiditySeconds(3600)
                // 配置刷新token的有效期
                .refreshTokenValiditySeconds(864000)
                // 配置redirect_uri,用于授权成功后的跳转
                // .redirectUris("http://www.baidu.com")
                // 单点登录时配置
                .redirectUris("http://localhost:9501/login")
                // 自动授权配置
                .autoApprove(true)
                // 配置申请的权限范围
                .scopes("all")
                // 配置grant_type,表示授权类型
                .authorizedGrantTypes("authorization_code", "password", "refresh_token");
    }
}

调用接口单点登录演示

这里我们使用Postman来演示下如何使用正确的方式调用需要登录的客户端接口。

访问客户端需要登录的接口:http://localhost:9501/user/getCurrentUser

使用Oauth2认证方式获取访问令牌:

在这里插入图片描述

输入获取访问令牌的相关信息,点击请求令牌:

在这里插入图片描述

此时会跳转到授权服务器进行登录操作:

在这里插入图片描述

登录成功后使用获取到的令牌:

在这里插入图片描述

最后请求接口可以获取到如下信息:

{
	"authorities": [{
		"authority": "admin"
	}],
	"details": {
		"remoteAddress": "0:0:0:0:0:0:0:1",
		"sessionId": "6F5A553BB678C7272145FF9FF2A5D8F4",
		"tokenValue": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJqb3Vyd29uIiwic2NvcGUiOlsiYWxsIl0sImV4cCI6MTU3NzY4OTc2NiwiYXV0aG9yaXRpZXMiOlsiYWRtaW4iXSwianRpIjoiZjAwYjVkMGUtNjFkYi00YjBmLTkyNTMtOWQxZDYwOWM4ZWZmIiwiY2xpZW50X2lkIjoiYWRtaW4iLCJlbmhhbmNlIjoiZW5oYW5jZSBpbmZvIn0.zdgFTWJt3DnAsjpQRU6rNA_iM7gVHX7E9bCyF73MOSM",
		"tokenType": "bearer",
		"decodedDetails": null
	},
	"authenticated": true,
	"userAuthentication": {
		"authorities": [{
			"authority": "admin"
		}],
		"details": null,
		"authenticated": true,
		"principal": "jourwon",
		"credentials": "N/A",
		"name": "jourwon"
	},
	"clientOnly": false,
	"oauth2Request": {
		"clientId": "admin",
		"scope": ["all"],
		"requestParameters": {
			"client_id": "admin"
		},
		"resourceIds": [],
		"authorities": [],
		"approved": true,
		"refresh": false,
		"redirectUri": null,
		"responseTypes": [],
		"extensions": {},
		"grantType": null,
		"refreshTokenRequest": null
	},
	"principal": "jourwon",
	"credentials": "",
	"name": "jourwon"
}

oauth2-client添加权限校验

添加配置开启基于方法的权限校验:

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
@Order(101)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
}

在UserController中添加需要admin权限的接口:

@RestController
@RequestMapping("/user")
public class UserController {

    @PreAuthorize("hasAuthority('admin')")
    @GetMapping("/auth/admin")
    public Object adminAuth() {
        return "Has admin auth!";
    }

}

访问需要admin权限的接口:http://localhost:9501/user/auth/admin

在这里插入图片描述

使用没有admin权限的帐号,比如andy:123456获取令牌后访问该接口,会发现没有权限访问。

在这里插入图片描述

使用到的模块

springcloud-learning
├── oauth2-jwt-server -- 使用jwt的oauth2认证测试服务
└── oauth2-client -- 单点登录的oauth2客户端服务

项目源码地址

GitHub项目源码地址

<think>好的,用户之前已经问过关于Java单点登录的案例,我提供了基于JWT和Redis的方案。现在用户进一步询问如何用SpringCloud实现单点登录。我需要确保回答在技术栈上正确,并且结合SpringCloud的组件。 首先,用户可能对SpringCloud的架构不太熟悉,需要先解释SpringCloud在SSO中的作用。然后,应该考虑使用Spring Cloud Gateway作为网关,Eureka做服务发现,Spring Security OAuth2处理认证授权,以及Redis做令牌存储。 接着,用户可能需要具体的架构设计和代码示例,所以需要分模块说明,比如认证中心、网关配置、客户端处理等。需要注意Spring Cloud版本,比如是否使用最新的2022.x版本,或者更稳定的Hoxton,这会影响依赖配置。 另外,用户可能关心单点登录的流程,比如如何通过网关路由到认证中心,令牌的颁发和验证过程,以及各个微服务之间如何协作。需要详细说明登录、验证、访问资源的过程,并确保安全措施如HTTPS、令牌刷新机制等被提及。 还要考虑用户可能遇到的常见问题,比如服务间的通信安全,令牌失效处理,分布式会话管理,以及如何扩展支持第三方登录。可能需要给出配置示例,比如Spring Security的设置,网关的路由规则,Redis的集成等。 最后,需要总结可能的优化方向,比如结合OAuth2、多因素认证等,确保用户有一个完整的实现方案,并能根据需求进行调整和扩展。</think>以下是一个基于Spring Cloud的分布式单点登录(SSO)系统实现案例,结合`OAuth2 + JWT + Spring Cloud Gateway`技术栈: --- ### 一、技术选型 | 组件 | 作用 | 版本要求 | |---------------------|-----------------------------|-------------| | Spring Cloud Gateway | 统一API网关 | 2022.0.4+ | | Eureka/Nacos | 服务注册与发现 | 2.7.x+ | | Spring Security | 安全认证框架 | 5.7.10+ | | Spring OAuth2 | 授权协议实现 | 2.6.8+ | | Redis | 令牌存储/会话管理 | 6.2+ | --- ### 二、架构设计 ``` 用户浏览器 │ ▼ [Spring Cloud Gateway] ←→ [认证中心] (oauth2-server) │ ▲ ▼ │ [微服务A] [微服务B] (资源服务器) ``` --- ### 三、核心实现步骤 #### 1. 认证中心配置(oauth2-server) ```yaml # application.yml spring: security: oauth2: client: registration: sso-client: client-id: sso-client client-secret: {bcrypt}$2a$10$... authorization-grant-type: authorization_code redirect-uri: http://gateway:8080/login/oauth2/code/sso redis: host: redis-cluster port: 6379 ``` #### 2. 网关统一路由配置 ```java @Configuration public class RouteConfig { @Bean public RouteLocator customRouteLocator(RouteLocatorBuilder builder) { return builder.routes() .route("auth_route", r -> r.path("/oauth2/**") .uri("lb://oauth2-server")) .route("serviceA_route", r -> r.path("/api/a/**") .filters(f -> f.tokenRelay()) .uri("lb://service-a")) .build(); } } ``` #### 3. JWT令牌增强配置 ```java @Configuration @EnableAuthorizationServer public class OAuth2Config extends AuthorizationServerConfigurerAdapter { @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) { endpoints.tokenStore(tokenStore()) .accessTokenConverter(jwtAccessTokenConverter()) .userDetailsService(userDetailsService); } @Bean public JwtAccessTokenConverter jwtAccessTokenConverter() { JwtAccessTokenConverter converter = new JwtAccessTokenConverter(); converter.setSigningKey("your-secret-key"); // HS256签名密钥 return converter; } } ``` --- ### 四、单点登录流程实现 #### 1. 登录时序 ```mermaid sequenceDiagram participant 用户 participant 网关 participant 认证中心 participant 微服务 用户->>网关: 访问受保护资源 网关->>用户: 302重定向到/login 用户->>认证中心: 提交登录凭证 认证中心->>Redis: 生成JWT令牌并存储 认证中心->>用户: 返回令牌+设置Cookie 用户->>网关: 携带令牌访问资源 网关->>认证中心: 验证令牌有效性 认证中心->>网关: 返回验证结果 网关->>微服务: 转发请求 微服务->>用户: 返回资源数据 ``` #### 2. 关键安全配置(资源服务器) ```java @Configuration @EnableResourceServer public class ResourceServerConfig extends ResourceServerConfigurerAdapter { @Override public void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/api/**").authenticated() .anyRequest().permitAll(); } @Override public void configure(ResourceServerSecurityConfigurer resources) { resources.tokenServices(tokenService()); } @Bean public RemoteTokenServices tokenService() { RemoteTokenServices service = new RemoteTokenServices(); service.setCheckTokenEndpointUrl("http://oauth2-server/oauth/check_token"); service.setClientId("sso-client"); service.setClientSecret("client-secret"); return service; } } ``` --- ### 五、分布式会话管理 #### 1. Redis会话存储配置 ```java @Configuration @EnableRedisHttpSession(maxInactiveIntervalInSeconds = 1800) public class SessionConfig { @Bean public RedisConnectionFactory redisConnectionFactory() { return new LettuceConnectionFactory("redis://redis-cluster:6379"); } } ``` #### 2. 跨域会话同步方案 ```java @Bean public FilterRegistrationBean<SessionRepositoryFilter<?>> sessionRepositoryFilter() { FilterRegistrationBean<SessionRepositoryFilter<?>> filter = new FilterRegistrationBean<>(); filter.setFilter(new SessionRepositoryFilter<>(redisSessionRepository())); filter.addUrlPatterns("/*"); return filter; } ``` --- ### 六、安全增强措施 1. **防御CSRF攻击** ```java @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()); } ``` 2. **令牌自动刷新机制** ```java @Bean public TokenRelayGatewayFilterFactory tokenRelayFilter(OAuth2AuthorizedClientService clientService) { return new TokenRelayGatewayFilterFactory(clientService); } ``` 3. **审计日志记录** ```java @EventListener public void auditListener(AbstractAuthenticationEvent event) { log.info("用户认证事件:{} - {}", event.getAuthentication().getName(), event.getClass().getSimpleName()); } ``` --- ### 七、扩展方案 1. **多租户支持** - 动态客户端注册 - 租户隔离数据库策略 2. **第三方登录集成** ```java @Bean public ClientRegistrationRepository clientRegistrations() { return new InMemoryClientRegistrationRepository( ClientRegistration.withRegistrationId("wechat") .clientId("wx-appid") .clientSecret("wx-secret") .authorizationUri("https://open.weixin.qq.com/connect/qrconnect") .tokenUri("https://api.weixin.qq.com/sns/oauth2/access_token") .build() ); } ``` 3. **风险控制** - 异地登录检测 - 设备指纹识别 - 登录频率限制 --- 该方案通过Spring Cloud生态组件实现了以下核心功能: 1. 统一认证入口(Spring Cloud Gateway) 2. 分布式会话管理(Redis + Spring Session) 3. 安全令牌中继(OAuth2 Token Relay) 4. 服务间安全通信(JWT签名验证) 5. 弹性扩展能力(Nacos服务发现) 实际部署时需要特别注意: - 网关层必须配置HTTPS - JWT签名密钥需要定期轮换 - Redis集群建议使用持久化模式 - 生产环境应禁用H2控制台等开发工具
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值