如需转载,请根据 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 许可,附上本文作者及链接。
本文作者: 执笔成念
作者昵称: zbcn
本文链接: https://1363653611.github.io/zbcn.github.io/2021/01/16/springcloud16-oauth2%E5%8D%95%E7%82%B9%E7%99%BB%E9%99%86/
Spring Cloud Security:Oauth2实现单点登录
Spring Cloud Security 为构建安全的SpringBoot应用提供了一系列解决方案,结合Oauth2可以实现单点登录功能,本文将对其单点登录用法进行详细介绍。
单点登录简介
单点登录(Single Sign On)指的是当有多个系统需要登录时,用户只需登录一个系统,就可以访问其他需要登录的系统而无需登录。
创建oauth2-client模块
这里我们创建一个oauth2-client服务作为需要登录的客户端服务,使用上一节中的oauth2-jwt-server服务作为认证服务,当我们在oauth2-jwt-server服务上登录以后,就可以直接访问oauth2-client需要登录的接口,来演示下单点登录功能。
- 在pom.xml中添加相关依赖:
1 | <dependency> |
2 | <groupId>org.springframework.cloud</groupId> |
3 | <artifactId>spring-cloud-starter-oauth2</artifactId> |
4 | </dependency> |
5 | <dependency> |
6 | <groupId>org.springframework.cloud</groupId> |
7 | <artifactId>spring-cloud-starter-security</artifactId> |
8 | </dependency> |
9 | <dependency> |
10 | <groupId>org.springframework.boot</groupId> |
11 | <artifactId>spring-boot-starter-web</artifactId> |
12 | </dependency> |
13 | <dependency> |
14 | <groupId>io.jsonwebtoken</groupId> |
15 | <artifactId>jjwt</artifactId> |
16 | <version>0.9.0</version> |
17 | </dependency> |
- 在application.yml中进行配置:
1 | server: |
2 | port: 9501 |
3 | servlet: |
4 | session: |
5 | cookie: |
6 | name: OAUTH2-CLIENT-SESSIONID #防止Cookie冲突,冲突会导致登录验证不通过 |
7 | oauth2-server-url: http://localhost:9401 |
8 | spring: |
9 | application: |
10 | name: oauth2-client |
11 | security: |
12 | oauth2: #与oauth2-server对应的配置 |
13 | client: |
14 | client-id: admin |
15 | client-secret: admin123456 |
16 | user-authorization-uri: ${oauth2-server-url}/oauth/authorize |
17 | access-token-uri: ${oauth2-server-url}/oauth/token |
18 | resource: |
19 | jwt: |
20 | key-uri: ${oauth2-server-url}/oauth/token_key |
- 在启动类上添加@EnableOAuth2Sso注解来启用单点登录功能:
1 | 2Sso |
2 |
|
3 | public class Oauth2ClientApplication { |
4 | |
5 | public static void main(String[] args) { |
6 | SpringApplication.run(Oauth2ClientApplication.class, args); |
7 | } |
8 | |
9 | } |
- 添加接口用于获取当前登录用户信息:
1 |
|
2 | "/user") ( |
3 | public class UserController { |
4 | |
5 | "/getCurrentUser") ( |
6 | public Object getCurrentUser(Authentication authentication) { |
7 | return authentication; |
8 | } |
9 | |
10 | } |
修改认证服务器配置
修改oauth2-jwt-server模块中的AuthorizationServerConfig类,将绑定的跳转路径为http://localhost:9501/login,并添加获取秘钥时的身份认证。
1 | /** |
2 | * 认证服务器配置 |
3 | */ |
4 |
|
5 |
|
6 | public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { |
7 | //以上省略一堆代码... |
8 | |
9 | public void configure(ClientDetailsServiceConfigurer clients) throws Exception { |
10 | clients.inMemory() |
11 | .withClient("admin") |
12 | .secret(passwordEncoder.encode("admin123456")) |
13 | .accessTokenValiditySeconds(3600) |
14 | .refreshTokenValiditySeconds(864000) |
15 | // .redirectUris("http://www.baidu.com") |
16 | .redirectUris("http://localhost:9501/login") //单点登录时配置 |
17 | .scopes("all") |
18 | .authorizedGrantTypes("authorization_code","password","refresh_token"); |
19 | } |
20 | |
21 | |
22 | public void configure(AuthorizationServerSecurityConfigurer security) { |
23 | security.tokenKeyAccess("isAuthenticated()"); // 获取密钥需要身份认证,使用单点登录时必须配置 |
24 | } |
25 | } |
网页单点登录演示
启动oauth2-client服务和oauth2-jwt-server服务;
访问客户端需要授权的接口http://localhost:9501/user/getCurrentUser会跳转到授权服务的登录界面;
进行登录操作后跳转到授权页面;
授权后会跳转到原来需要权限的接口地址,展示登录用户信息;
如果需要跳过授权操作进行自动授权可以添加
autoApprove(true)
配置:
1 | /** |
2 | * 认证服务器配置 |
3 | */ |
4 |
|
5 |
|
6 | public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { |
7 | //以上省略一堆代码... |
8 | |
9 | public void configure(ClientDetailsServiceConfigurer clients) throws Exception { |
10 | clients.inMemory() |
11 | .withClient("admin") |
12 | .secret(passwordEncoder.encode("admin123456")) |
13 | .accessTokenValiditySeconds(3600) |
14 | .refreshTokenValiditySeconds(864000) |
15 | // .redirectUris("http://www.baidu.com") |
16 | .redirectUris("http://localhost:9501/login") //单点登录时配置 |
17 | .autoApprove(true) //自动授权配置 |
18 | .scopes("all") |
19 | .authorizedGrantTypes("authorization_code","password","refresh_token"); |
20 | } |
21 | } |
调用接口单点登录演示
这里我们使用Postman来演示下如何使用正确的方式调用需要登录的客户端接口。
- 访问客户端需要登录的接口:http://localhost:9501/user/getCurrentUser
- 使用Oauth2认证方式获取访问令牌:
配置信息
1 | Callback URL: http://localhost:9501/login |
2 | Auth URL: http://localhost:9401/oauth/authorize |
3 | Access Token URL: http://localhost:9401/oauth/token |
4 | Client ID: admin |
5 | Client Secret: admin123456 |
6 | Scope:all |
7 | State:normal |
- 此时会跳转到认证服务器进行登录操作:
- 登录成功后使用获取到的令牌:
- 最后请求接口可以获取到如下信息:
oauth2-client添加权限校验
- 添加配置开启基于方法的权限校验:
1 | /** |
2 | * 在接口上配置权限时使用 |
3 | */ |
4 |
|
5 | true) (prePostEnabled = |
6 | 101) ( |
7 | public class SecurityConfig extends WebSecurityConfigurerAdapter { |
8 | } |
- 在UserController中添加需要admin权限的接口:
1 | /** |
2 | * Created by macro on 2019/9/30. |
3 | */ |
4 |
|
5 | "/user") ( |
6 | public class UserController { |
7 | |
8 | "hasAuthority('admin')") ( |
9 | "/auth/admin") ( |
10 | public Object adminAuth() { |
11 | return "Has admin auth!"; |
12 | } |
13 | |
14 | } |
访问需要admin权限的接口:http://localhost:9501/user/auth/admin
使用没有
admin
权限的帐号,比如andy:123456
获取令牌后访问该接口,会发现没有权限访问。
使用到的模块
1 | ZBCN-SERVER |
2 | ├── zbcn-author/oauth2-client -- -- 单点登录的oauth2客户端服务 |
3 | └── zbcn-author/oauth2-jwt-server -- 使用jwt的oauth2认证测试服务 |