官方文档
Spring Authorization Server
环境介绍
java:17
SpringBoot:3.2.0
SpringCloud:2023.0.0
引入maven配置
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-authorization-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
AuthorizationServerConfig认证中心配置类
package com.auth.config;
import com.jilianyun.exception.ServiceException;
import com.nimbusds.jose.jwk.JWKSet;
import com.nimbusds.jose.jwk.RSAKey;
import com.nimbusds.jose.jwk.source.ImmutableJWKSet;
import com.nimbusds.jose.jwk.source.JWKSource;
import com.nimbusds.jose.proc.SecurityContext;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.server.authorization.client.JdbcRegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers.OAuth2AuthorizationServerConfigurer;
import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;
import org.springframework.security.oauth2.server.authorization.settings.ClientSettings;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.MediaTypeRequestMatcher;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.UUID;
/**
* <p>认证中心配置类</p>
*
* @author By: chengxuyuanshitang
* Ceate Time 2024-05-07 11:42
*/
@Slf4j
@EnableWebSecurity
@Configuration(proxyBeanMethods = false)
public class AuthorizationServerConfig {
/**
* Security过滤器链,用于协议端点
*
* @param http HttpSecurity
* @return SecurityFilterChain
*/
@Bean
public SecurityFilterChain authorizationServerSecurityFilterChain (HttpSecurity http) throws Exception {
OAuth2AuthorizationServerConfiguration.applyDefaultSecurity (http);
http.getConfigurer (OAuth2AuthorizationServerConfigurer.class)
//启用OpenID Connect 1.0
.oidc (Customizer.withDefaults ());
http
// 未从授权端点进行身份验证时重定向到登录页面
.exceptionHandling ((exceptions) -> exceptions
.defaultAuthenticationEntryPointFor (
new LoginUrlAuthenticationEntryPoint ("/login"),
new MediaTypeRequestMatcher (MediaType.TEXT_HTML)
)
)
//接受用户信息和/或客户端注册的访问令牌
.oauth2ResourceServer ((resourceServer) -> resourceServer
.jwt (Customizer.withDefaults ()));
return http.build ();
}
/**
* 用于认证的Spring Security过滤器链。
*
* @param http HttpSecurity
* @return SecurityFilterChain
*/
@Bean
public SecurityFilterChain defaultSecurityFilterChain (HttpSecurity http) throws Exception {
http.authorizeHttpRequests ((authorize) -> authorize
.requestMatchers (new AntPathRequestMatcher ("/actuator/**"),
new AntPathRequestMatcher ("/oauth2/**"),
new AntPathRequestMatcher ("/**/*.json"),
new AntPathRequestMatcher ("/**/*.css"),
new AntPathRequestMatcher ("/**/*.html")).permitAll ()
.anyRequest ().authenticated ()
)
.formLogin (Customizer.withDefaults ());
return http.build ();
}
/**
* 配置密码解析器,使用BCrypt的方式对密码进行加密和验证
*
* @return BCryptPasswordEncoder
*/
@Bean
public PasswordEncoder passwordEncoder () {
return new BCryptPasswordEncoder ();
}
@Bean
public UserDetailsService userDetailsService () {
UserDetails userDetails = User.withUsername ("chengxuyuanshitang")
.password (passwordEncoder ().encode ("chengxuyuanshitang"))
.roles ("admin")
.build ();
return new InMemoryUserDetailsManager (userDetails);
}
/**
* RegisteredClientRepository 的一个实例,用于管理客户端
*
* @param jdbcTemplate jdbcTemplate
* @param passwordEncoder passwordEncoder
* @return RegisteredClientRepository
*/
@Bean
public RegisteredClientRepository registeredClientRepository (JdbcTemplate jdbcTemplate, PasswordEncoder passwordEncoder) {
RegisteredClient registeredClient = RegisteredClient.withId (UUID.randomUUID ().toString ())
.clientId ("oauth2-client")
.clientSecret (passwordEncoder.encode ("123456"))
// 客户端认证基于请求头
.clientAuthenticationMethod (ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
// 配置授权的支持方式
.authorizationGrantType (AuthorizationGrantType.AUTHORIZATION_CODE)
.authorizationGrantType (AuthorizationGrantType.REFRESH_TOKEN)
.authorizationGrantType (AuthorizationGrantType.CLIENT_CREDENTIALS)
.redirectUri ("https://www.baidu.com")
.scope ("user")
.scope ("admin")
// 客户端设置,设置用户需要确认授权
.clientSettings (ClientSettings.builder ().requireAuthorizationConsent (true).build ())
.build ();
JdbcRegisteredClientRepository registeredClientRepository = new JdbcRegisteredClientRepository (jdbcTemplate);
RegisteredClient repositoryByClientId = registeredClientRepository.findByClientId (registeredClient.getClientId ());
if (repositoryByClientId == null) {
registeredClientRepository.save (registeredClient);
}
return registeredClientRepository;
}
/**
* 用于签署访问令牌
*
* @return JWKSource
*/
@Bean
public JWKSource<SecurityContext> jwkSource () {
KeyPair keyPair = generateRsaKey ();
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic ();
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate ();
RSAKey rsaKey = new RSAKey.Builder (publicKey)
.privateKey (privateKey)
.keyID (UUID.randomUUID ().toString ())
.build ();
JWKSet jwkSet = new JWKSet (rsaKey);
return new ImmutableJWKSet<> (jwkSet);
}
/**
* 创建RsaKey
*
* @return KeyPair
*/
private static KeyPair generateRsaKey () {
KeyPair keyPair;
try {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance ("RSA");
keyPairGenerator.initialize (2048);
keyPair = keyPairGenerator.generateKeyPair ();
} catch (Exception e) {
log.error ("generateRsaKey Exception", e);
throw new ServiceException ("generateRsaKey Exception");
}
return keyPair;
}
/**
* 解码签名访问令牌
*
* @param jwkSource jwkSource
* @return JwtDecoder
*/
@Bean
public JwtDecoder jwtDecoder (JWKSource<SecurityContext> jwkSource) {
return OAuth2AuthorizationServerConfiguration.jwtDecoder (jwkSource);
}
@Bean
public AuthorizationServerSettings authorizationServerSettings () {
return AuthorizationServerSettings.builder ().build ();
}
}
详细介绍
SecurityFilterChain authorizationServerSecurityFilterChain (HttpSecurity http)
Spring Security的过滤器链,用于协议端点
SecurityFilterChain defaultSecurityFilterChain (HttpSecurity http)
Security的过滤器链,用于Security的身份认证
PasswordEncoder passwordEncoder ()
配置密码解析器,使用BCrypt的方式对密码进行加密和验证
UserDetailsService userDetailsService ()
用于进行用户身份验证
RegisteredClientRepository registeredClientRepository (JdbcTemplate jdbcTemplate, PasswordEncoder passwordEncoder)
用于管理客户端
JWKSource<SecurityContext> jwkSource ()
用于签署访问令牌
KeyPair generateRsaKey ()
创建RsaKey
JwtDecoder jwtDecoder (JWKSource<SecurityContext> jwkSource)
解码签名访问令牌
AuthorizationServerSettings authorizationServerSettings ()
配置Spring Authorization Server的AuthorizationServerSettings实例
初始化自带的数据表
自带的表在spring-security-oauth2-authorization-server-1.2.0.jar 中 下面是对应的截图
对应的SQL
-- 已注册的客户端信息表
CREATE TABLE oauth2_registered_client
(
id varchar(100) NOT NULL,
client_id varchar(100) NOT NULL,
client_id_issued_at timestamp DEFAULT CURRENT_TIMESTAMP NOT NULL,
client_secret varchar(200) DEFAULT NULL,
client_secret_expires_at timestamp DEFAULT NULL,
client_name varchar(200) NOT NULL,
client_authentication_methods varchar(1000) NOT NULL,
authorization_grant_types varchar(1000) NOT NULL,
redirect_uris varchar(1000) DEFAULT NULL,
post_logout_redirect_uris varchar(1000) DEFAULT NULL,
scopes varchar(1000) NOT NULL,
client_settings varchar(2000) NOT NULL,
token_settings varchar(2000) NOT NULL,
PRIMARY KEY (id)
);
-- 认证授权表
CREATE TABLE oauth2_authorization_consent
(
registered_client_id varchar(100) NOT NULL,
principal_name varchar(200) NOT NULL,
authorities varchar(1000) NOT NULL,
PRIMARY KEY (registered_client_id, principal_name)
);
/*
IMPORTANT:
If using PostgreSQL, update ALL columns defined with 'blob' to 'text',
as PostgreSQL does not support the 'blob' data type.
*/
-- 认证信息表
CREATE TABLE oauth2_authorization
(
id varchar(100) NOT NULL,
registered_client_id varchar(100) NOT NULL,
principal_name varchar(200) NOT NULL,
authorization_grant_type varchar(100) NOT NULL,
authorized_scopes varchar(1000) DEFAULT NULL,
attributes blob DEFAULT NULL,
state varchar(500) DEFAULT NULL,
authorization_code_value blob DEFAULT NULL,
authorization_code_issued_at timestamp DEFAULT NULL,
authorization_code_expires_at timestamp DEFAULT NULL,
authorization_code_metadata blob DEFAULT NULL,
access_token_value blob DEFAULT NULL,
access_token_issued_at timestamp DEFAULT NULL,
access_token_expires_at timestamp DEFAULT NULL,
access_token_metadata blob DEFAULT NULL,
access_token_type varchar(100) DEFAULT NULL,
access_token_scopes varchar(1000) DEFAULT NULL,
oidc_id_token_value blob DEFAULT NULL,
oidc_id_token_issued_at timestamp DEFAULT NULL,
oidc_id_token_expires_at timestamp DEFAULT NULL,
oidc_id_token_metadata blob DEFAULT NULL,
refresh_token_value blob DEFAULT NULL,
refresh_token_issued_at timestamp DEFAULT NULL,
refresh_token_expires_at timestamp DEFAULT NULL,
refresh_token_metadata blob DEFAULT NULL,
user_code_value blob DEFAULT NULL,
user_code_issued_at timestamp DEFAULT NULL,
user_code_expires_at timestamp DEFAULT NULL,
user_code_metadata blob DEFAULT NULL,
device_code_value blob DEFAULT NULL,
device_code_issued_at timestamp DEFAULT NULL,
device_code_expires_at timestamp DEFAULT NULL,
device_code_metadata blob DEFAULT NULL,
PRIMARY KEY (id)
);
application.yml中数据库配置
spring:
profiles:
active: dev
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://192.168.0.1:3306/auth?characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true
username: auth
password: 12345
启动AuthServerApplication
启动完成后查看数据库的oauth2_registered_client表中有一条数据;
查看授权服务配置
地址:http://127.0.0.1:8801/.well-known/openid-configuration
访问/oauth2/authorize前往登录页面
地址:ip/端口/oauth2/authorize?client_id=app-client&response_type=code&scope=user&redirect_uri=https://www.baidu.com
实例:
http://127.0.0.1:8801/oauth2/authorize?client_id=app-client&response_type=code&scope=user&redirect_uri=https://www.baidu.com
浏览器跳转到:http://127.0.0.1:8801/login
输入上面配置的密码和账号。我这里都是:chengxuyuanshitang 点击提交。跳转
跳转到
网址栏的地址:https://www.baidu.com/?code=S73PUvl26OSxBc-yBbRRPJMTzcvE2x-VFZGXFPjpvOXHrecbY3Thsj6aOxPdN31H4a6GUgujSc1D4lj9D1ApIUAfZi55YJLqiRLpCivb-Is_4h3grILgR8H8M9UWyhJt
code的值就是=后面的
code=S73PUvl26OSxBc-yBbRRPJMTzcvE2x-VFZGXFPjpvOXHrecbY3Thsj6aOxPdN31H4a6GUgujSc1D4lj9D1ApIUAfZi55YJLqiRLpCivb-Is_4h3grILgR8H8M9UWyhJt
获取token
请求地址:http://127.0.0.1:8801/oauth2/token?grant_type=authorization_code&redirect_uri=https://www.baidu.com&code=S73PUvl26OSxBc-yBbRRPJMTzcvE2x-VFZGXFPjpvOXHrecbY3Thsj6aOxPdN31H4a6GUgujSc1D4lj9D1ApIUAfZi55YJLqiRLpCivb-Is_4h3grILgR8H8M9UWyhJt
用postman请求
添加header参数
header中的 Authorization参数:因为我们用的客户端认证方式 为 client_secret_basic ,这个需要传参,还有一些其他的认证方式,
client_secret_basic: 将 clientId 和 clientSecret 通过 : 号拼接,并使用 Base64 进行编码得到一串字符,再在前面加个 注意有个 Basic 前缀(Basic后有一个空格), 即得到上面参数中的 Basic 。
我的是:app-client:123456
Base64 进行编码:YXBwLWNsaWVudDoxMjM0NTY=
返回:
{
"access_token": "eyJraWQiOiI2ZTJmYTA5ZS0zMmYzLTQ0MmQtOTM4Zi0yMzJjNDViYTM1YmMiLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJjaGVuZ3h1eXVhbnNoaXRhbmciLCJhdWQiOiJhcHAtY2xpZW50IiwibmJmIjoxNzE1MDcxOTczLCJzY29wZSI6WyJ1c2VyIl0sImlzcyI6Imh0dHA6Ly8xMjcuMC4wLjE6ODgwMSIsImV4cCI6MTcxNTA3MjI3MywiaWF0IjoxNzE1MDcxOTczLCJqdGkiOiI0MWI4ZGZmZS03MTI2LTQ4NWYtODRmYy00Yjk4OGE0N2ZlMzUifQ.VxP2mLHt-eyXHZOI36yhVlwC2UQEdAtaRBKTWwJn1bFup0ZjGbZfgxENUb1c03yjcki2H-gCW4Jgef11BMNtjyWSnwMHVWLB9fcT3rRKDQWwoWqBYAcULS8oC5n8qTZwffDSrnjepMEbw4CblL3oH7T9nLProTXQP326RIE1RczsUYkteUCkyIvKTSs3ezOjIVR1GyCs_Cl1A_3OllmkGnSO2q-NKkwasrQjMuuPTY3nhDyDGiefYlfDEcmzz1Yk_FE42P7PEeyqmZwAj7vUnE4brQuNqipaMsS7INe_wTE1kJv-arfbnUo_zQdipHxIhsDgoLaPlSSecQ31QgwEHA",
"refresh_token": "TqJyWbLWe5Yww6dOV89zDbO0C3YEBA__0TJU_GclmQTAH92SSQ2OvdMChIdln97u1WsA7G7n3BqzNZBjPRU7xmkRooa5ifsMBJ-d3C4kPmuPQI-Bmbq20pck-QEk0Dqt",
"scope": "user",
"token_type": "Bearer",
"expires_in": 300
}
访问接口/userinfo
请求地址:http://127.0.0.1:8801/userinfo
添加header参数:Authorization: Bearer +空格+ ${access_token}