oauth入门

认证服务器

依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-security</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
<version>2.2.5.RELEASE</version><!-- 这一行不加可能会报错 -->
</dependency>

<!-- seata和oauth的神奇反应 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
<scope>test</scope>
</dependency>

认证服务器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
package cn.sdadgz.oauth;

import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
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.code.AuthorizationCodeServices;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;

/**
* 授权服务器
*
* <p>
* 废物本物
* </p>
*
* @author sdadgz
* @since 2023/3/8 16:39:17
*/
@Configuration
@EnableAuthorizationServer
@RequiredArgsConstructor
public class AuthorizationServer extends AuthorizationServerConfigurerAdapter {

private final AuthorizationCodeServices authorizationCodeServices;
private final AuthenticationManager authenticationManager;
private final UserDetailsService userDetailsService;
private final AuthorizationServerTokenServices tokenServices;
private final PasswordEncoder passwordEncoder;

@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
// 客户端详情服务
clients.inMemory()
.withClient("client1") // 客户端名字
.secret(passwordEncoder.encode("123456")) // 密文
.resourceIds("/test") // 拥有的资源列表
.authorizedGrantTypes("authorization_code", "password", "client_credentials", "implicit", "refresh_token") // 允许的授权方式
.scopes("all") // 允许的授权范围
.autoApprove(false) // false,手动点授权
.redirectUris("https://sdadgz.cn"); // 回调地址
}

@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
.authenticationManager(authenticationManager)
.userDetailsService(userDetailsService)
.authorizationCodeServices(authorizationCodeServices)
.tokenServices(tokenServices)
.allowedTokenEndpointRequestMethods(HttpMethod.POST);
}

@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security
.tokenKeyAccess("permitAll()") // oauth/token_key
.checkTokenAccess("permitAll()") // oauth/check_token
.allowFormAuthenticationForClients();
}

}

token存储

uuid存储

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package cn.sdadgz.oauth;

import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore;

/**
* 令牌配置
*
* <p>
* 废物本物
* </p>
*
* @author sdadgz
* @since 2023/3/8 18:47:58
*/
@Configuration
@RequiredArgsConstructor
public class TokenConfig {

@Bean
public TokenStore tokenStore() {
return new InMemoryTokenStore();
}

}

jwt存储

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package cn.sdadgz.oauth;

import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;

/**
* 令牌配置
*
* <p>
* 废物本物
* </p>
*
* @author sdadgz
* @since 2023/3/8 18:47:58
*/
@Configuration
@RequiredArgsConstructor
public class TokenConfig {

public static final String SIGN_KEY = "password";

@Bean
public TokenStore tokenStore(){
return new JwtTokenStore(accessTokenConverter());
}

@Bean
public JwtAccessTokenConverter accessTokenConverter(){
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey(SIGN_KEY);
return converter;
}

}

UserDetails用户

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
package cn.sdadgz.oauth;

import cn.hutool.extra.spring.SpringUtil;
import lombok.NoArgsConstructor;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.password.PasswordEncoder;

import java.util.Collection;

/**
* 登录用的角色
*
* <p>
* 废物本物
* </p>
*
* @author sdadgz
* @since 2023/3/26 15:21:19
*/
@NoArgsConstructor
public class OauthUser implements UserDetails {

private static final PasswordEncoder passwordEncoder = SpringUtil.getBean(PasswordEncoder.class);

@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return null;
}

@Override
public String getPassword() {
return passwordEncoder.encode("123456");
}

@Override
public String getUsername() {
return "root";
}

@Override
public boolean isAccountNonExpired() {
return true;
}

@Override
public boolean isAccountNonLocked() {
return true;
}

@Override
public boolean isCredentialsNonExpired() {
return true;
}

@Override
public boolean isEnabled() {
return true;
}
}

UserDetialsService用户查询

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package cn.sdadgz.oauth;

import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

/**
* 用户详情实现类
*
* <p>
* 废物本物
* </p>
*
* @author sdadgz
* @since 2023/3/26 14:41:50
*/
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return new OauthUser();
}
}

WebsecurityConfig安全配置

正常配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
package cn.sdadgz.oauth;

import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
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;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
import org.springframework.security.oauth2.provider.code.InMemoryAuthorizationCodeServices;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;

/**
* 安全配置
*
* <p>
* 废物本物
* </p>
*
* @author sdadgz
* @since 2023/3/22 20:46:04
*/
@Configuration
@RequiredArgsConstructor
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

private final TokenStore tokenStore;
private final ClientDetailsService clientDetailsService;

@Bean
public AuthorizationServerTokenServices tokenServices() {
DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
defaultTokenServices.setClientDetailsService(clientDetailsService);
defaultTokenServices.setSupportRefreshToken(true);
defaultTokenServices.setTokenStore(tokenStore);
defaultTokenServices.setAccessTokenValiditySeconds(5673);
defaultTokenServices.setRefreshTokenValiditySeconds(123456);
return defaultTokenServices;
}

@Bean
public AuthorizationCodeServices authorizationCodeServices(){
return new InMemoryAuthorizationCodeServices();
}

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

@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}

@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin();
}
}

jwt配置

defaultTokenServices.setTokenStore(tokenStore);后面加上

defaultTokenServices.setTokenEnhancer(accessTokenConverter);

资源服务器

ResourceServerConfig资源服务器配置

正常配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
package cn.sdadgz.resourceServer;

import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices;

/**
* 资源服务器配置
*
* <p>
* 废物本物
* </p>
*
* @author sdadgz
* @since 2023/3/26 16:45:16
*/
@EnableResourceServer
@Configuration
@RequiredArgsConstructor
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {

private final ResourceServerTokenServices tokenService;

@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources
.resourceId("/test")
.tokenServices(tokenService) // 验证令牌
.stateless(true); // 无状态,不存token
}

@Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/**")
.access("#oauth2.hasScope('all')")
.and().csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
}

jwt配置

注释掉.tokenServices(tokenService),加上

.tokenStore(tokenStore)

WebSecurity安全配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package cn.sdadgz.resourceServer;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.oauth2.provider.token.RemoteTokenServices;
import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices;

/**
* 安全配置
*
* <p>
* 废物本物
* </p>
*
* @author sdadgz
* @since 2023/3/26 16:54:32
*/
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

@Bean
public ResourceServerTokenServices tokenServices(){
RemoteTokenServices services = new RemoteTokenServices();
services.setCheckTokenEndpointUrl("http://localhost:30002/oauth/check_token");
services.setClientId("client1");
services.setClientSecret("123456");
return services;
}

@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/test/**")
.authenticated()
.anyRequest().permitAll();
}
}

认证模式

client_credentials客户端模式

不安全,内部系统使用

1
{{base_url}}/oauth/token?client_id=client1&client_secret=123456&grant_type=client_credentials

password密码模式

安全了一点,还是内部