SpringSecurity-OAuth2 授权
SpringSecurity-OAuth2 授权
授权模式复杂程度由大至小:授权码模式 > 隐式授权模式 > 密码模式 > 客户端模式
(1)客户端模式
其中客户端模式的流程是:客户端使用授权服器给的标识与secret访问资源服务器获取token
没有用户与授权服务器交互,完全以机器形式获取授权与使用授权,客户端机器 <-> 授权服务器,这种方式主要用于第一方应用中
(2)密码模式
其中密码模式的流程是:让用户填写表单提交到授权服务器,表单中包含用户的用户名、密码、客户端的id和密钥的加密串,授权服务器先解析并校验客户端信息,然后校验用户信息,完全通过返回access_token,否则默认都是401 http状态码,提示未授权无法访问
(3) 隐式授权模式
适用场景有以下几个条件:
- 用户参与:使用隐式授权需要与用户交互,用户对授权服务器进行登录与授权
- 单页应用:SPA前端,没有后端或者后端属于授权方
- 客户端密码:访问授权时,不需要带第三方应用secret,前提是资源服务校验token使用的client信息与客户端(第三方应用)不同,且配置了secret
- 前端:必须要有前端,否则无法使用授权功能
- 客户端后端:Options,仅当应用前后端不分离MVC场景
- 资源所属方:授权方
(4)授权码模式
授权码模式要求:用户登录并对第三方应用(客户端)进行授权,出示授权码交给客户端,客户端凭授权码换取access_token(访问凭证)
此模式要求授权服务器与用户直接交互,在此过程中,第三方应用是无法获取到用户输入的密码等信息的,这个模式也是OAuth 2.0中最安全的一个
登录用户/密码: hellxz/xyz ,选择Approve表示接受授权,Deny反之
http://localhost:9001/callback?code=4mak0O
http://localhost:8080/oauth/token
BasicAuth:这里填的是客户端配置的client_id和client_secret的值,相当于curl --user client_id:client_secret,配置后会在Header中添加Authorization:Basic Y2xpZW50LWE6Y2xpZW50LWEtc2VjcmV0,Basic空格 后的是client_id:client_secret具体值被Base64后得到的值
请求参数列表:
- code=授权码
- grant_type=authorization_code
- redirect_uri=回调url ,要与配置处和获取授权码处相同
- scope=作用域
http://localhost:8081/user/hellxz001
WebSecurityConfigurerAdapter
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("hellxz")
.password(passwordEncoder().encode("xyz"))
.authorities(new ArrayList<>(0));
}
WebSecurityConfigurerAdapter
AuthorizationServerConfigurerAdapter
AuthorizationServerConfigurerAdapter
//配置客户端
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
//@formatter:off
clients.inMemory()
.withClient("client-a")
.secret(passwordEncoder.encode("client-a-secret"))
.authorizedGrantTypes("password")
.scopes("read_scope");
//@formatter:on
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
// @formatter: off
clients.inMemory()
.withClient("client-a") //client端唯一标识
.authorizedGrantTypes("implicit") //授权模式标识
.accessTokenValiditySeconds(120) //访问令牌的有效期,这里设置120s
.scopes("read_user_info") //作用域
.resourceIds("resource1") //资源id
.redirectUris("http://localhost:9001/callback") //回调地址
.and()
.withClient("resource-server") //资源服务器校验token时用的客户端信息,仅需要client_id与密码
.secret(passwordEncoder.encode("test"));
// @formatter: on
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
// @formatter: off
clients.inMemory()
.withClient("client-a") //client端唯一标识
.secret(passwordEncoder.encode("client-a-secret")) //client-a的密码,这里的密码应该是加密后的
.authorizedGrantTypes("client_credentials","password", "refresh_token") //授权模式标识,开启刷新token功能
.scopes("read_user_info", "service", "users") //作用域
.resourceIds("resource1") //资源id,如不需限制资源id,注释此处即可
.redirectUris("http://localhost:9001/callback"); //回调地址
// @formatter: on
}
资源服务器只做token的校验与给予资源
ResourceServerConfigurerAdapter
ResourceServerConfigurerAdapter
@Bean
public RemoteTokenServices remoteTokenServices() {
final RemoteTokenServices tokenServices = new RemoteTokenServices();
tokenServices.setClientId("client-a");
tokenServices.setClientSecret("client-a-secret");
tokenServices.setCheckTokenEndpointUrl("http://localhost:8080/oauth/check_token");
return tokenServices;
}
@Primary
@Bean
public RemoteTokenServices remoteTokenServices() {
final RemoteTokenServices tokenServices = new RemoteTokenServices();
tokenServices.setCheckTokenEndpointUrl("http://localhost:8080/oauth/check_token");
//这里的clientId和secret对应资源服务器信息,授权服务器处需要配置
tokenServices.setClientId("resource-server");
tokenServices.setClientSecret("test");
return tokenServices;
}
本文仅说明密码模式的精简化配置,某些部分如资源服务再访问授权服务去校验token这部分生产环境可能会换成Jwt、Redis等tokenStore实现,授权服务器中的用户信息与客户端信息生产环境应从数据库中读取,对应Spring Security的UserDetailsService实现类或用户信息的Provider等
请求参数列表:
- client_id=客户端id
- redirect_uri=回调url 一定要与授权服务器配置保持一致,否则得不到授权码
- response_type=token 简化模式必须是token
- scope=作用域 与授权服务器配置保持一致
- state=自定义串(可选)
OAuth2定义了四种授权模式(授权流程)来对资源的访问进行控制
授权码模式(Authorization Code Grant)
隐式授权模式(Implicit Grant)
用户名密码模式(Resource Owner Password Credentials Grant)
客户端模式(Client Credentials Grant)
无论哪个模式(流程)都拥有三个必要角色:客户端、授权服务器、资源服务器,有的还有用户(资源拥有者)
keytool -genkeypair -alias hellxz-jwt -validity 3650 -keyalg RSA -keypass hellxzTest -keystore hellxz-jwt.jks -storepass hellxzTest
➜ keytool -list -rfc --keystore hellxz-jwt.jks | openssl x509 -inform pem -pubkey
输入密钥库口令: hellxzTest
netstat -ano | findstr [端口号]
netstat -ano | findstr 19003
netstat -ano | findstr 9003
netstat -ano | findstr 9007
另外使用JWT应设置尽量短的过期时间,因为JWT的token无法手动revoke,只能等待其到达过期时间失效
/**
* 客户端信息配置,可配置多个客户端,这里可以使用配置文件进行代替
*
* @param clients 客户端设置
* @throws Exception 异常
*/
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("client-a")
.secret(passwordEncoder.encode("client-a-secret"))
.redirectUris("http://localhost:9001/callback")
//支持 授权码、密码两种授权模式,支持刷新token功能
.authorizedGrantTypes("authorization_code", "password", "refresh_token");
}
/**
* 配置端点
*
* @param endpoints 端点
* @throws Exception 异常
*/
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
//配置认证管理器
endpoints.authenticationManager(authenticationManager)
//配置用户服务
.userDetailsService(userDetailsService)
//配置token存储的服务与位置
.tokenServices(tokenService())
.tokenStore(tokenStore());
}
@Bean
public TokenStore tokenStore() {
//使用redis存储token
RedisTokenStore redisTokenStore = new RedisTokenStore(redisConnectionFactory);
//设置redis token存储中的前缀
redisTokenStore.setPrefix("auth-token:");
return redisTokenStore;
}
@Bean
public DefaultTokenServices tokenService() {
DefaultTokenServices tokenServices = new DefaultTokenServices();
//配置token存储
tokenServices.setTokenStore(tokenStore());
//开启支持refresh_token,此处如果之前没有配置,启动服务后再配置重启服务,可能会导致不返回token的问题,解决方式:清除redis对应token存储
tokenServices.setSupportRefreshToken(true);
//复用refresh_token
tokenServices.setReuseRefreshToken(true);
//token有效期,设置12小时
tokenServices.setAccessTokenValiditySeconds(12 * 60 * 60);
//refresh_token有效期,设置一周
tokenServices.setRefreshTokenValiditySeconds(7 * 24 * 60 * 60);
return tokenServices;
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
// 配置客户端
clients
// 使用内存设置
.inMemory()
// client_id
.withClient("client")
// client_secret
.secret(passwordEncoder.encode("secret"))
// 授权类型: 授权码、刷新令牌、密码、客户端、简化模式、短信验证码 "refresh_token"
.authorizedGrantTypes("authorization_code", "password", "client_credentials", "implicit", "sms_code")
// 授权范围,也可根据这个范围标识,进行鉴权
.scopes("admin:org","write:org","read:org")
.accessTokenValiditySeconds(300)
.refreshTokenValiditySeconds(3000)
// 授权码模式 授权页面是否自动授权
//.autoApprove(false)
// 拥有的权限
.authorities("add:user")
// 允许访问的资源服务 ID
//.resourceIds("oauth2-resource-server001-demo")
// 注册回调地址
.redirectUris("http://localhost:20000/code");
}
Spring boot——Actuator 详解
https://blog.csdn.net/weixin_45985053/article/details/125872921
https://zhuanlan.zhihu.com/p/343095559
SpringBoot整合可视化监控工具——SpringBoot Admin
https://zhuanlan.zhihu.com/p/539661461
https://cloud.tencent.com/developer/article/2145410
https://blog.csdn.net/jiangjun_dao519/article/details/125242434
https://blog.csdn.net/goodjava2007/article/details/126395140
keytool
keytool为java原生自带,安装java后不需要再进行安装,作为密钥和证书管理工具,方便用户能够管理自己的公钥/私钥及证书,用于认证服务。
https://blog.csdn.net/Guesshat/article/details/123024693
keytool -genkeypair -alias hellxz-jwt -validity 3650 -keyalg RSA -keypass hellxzTest -keystore hellxz-jwt.jks -storepass hellxzTest
➜ keytool -list -rfc --keystore hellxz-jwt.jks | openssl x509 -inform pem -pubkey
输入密钥库口令: hellxzTest
keytool -genkeypair -alias hsAdmin-jwt-dev -validity 3650 -keyalg RSA -keypass adminDev -keystore hsAdmin-jwt-dev.jks -storepass adminDev
➜ keytool -list -rfc --keystore hsAdmin-jwt-dev.jks | openssl x509 -inform pem -pubkey
输入密钥库口令: adminDev
keytool -list -v -keystore hsAdmin-jwt-dev.jks -storepass adminDev
参考
https://www.cnblogs.com/hellxz/p/oauth2_process.html
JWT简要说明
使用Redis作为Spring Security OAuth2的token存储
maven的pom文件中的作用
https://blog.csdn.net/Guesshat/article/details/123024693
JKS和PKCS#12

浙公网安备 33010602011771号