授权码模式:
1.需要提供appid和app密码
2.配置回调地址
3.验证token接口
实现流程:
1.引入必要依赖:
security
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
oauth2
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<version>2.2.1.RELEASE</version>
</dependency>
jwt
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
2.修改security配置
完成这个部分(拦截+用户校验部分)
新增授权类AuthorizationConfig
继承AuthorizationServerConfigurerAdapter
,
重写他的两个配置方法,
在类中注入我们配置好的加密类:
@Component
@EnableAuthorizationServer
public class AuthorizationConfig extends AuthorizationServerConfigurerAdapter{
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
}
}
在AuthorizationServerSecurityConfigurer
参数的方法中配置允许表单提交
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security.allowFormAuthenticationForClients().checkTokenAccess("permitAll()");
}
在ClientDetailsServiceConfigurer
参数的方法中配置
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
//appid
.withClient("gaga")
//appsecret
.secret(passwordEncoder.encode("gaga_test_security"))
//授权码
.authorizedGrantTypes("authorization_code")
//作用域
.scopes("all")
//资源id
.resourceIds("employed_resource")
//回调地址
.redirectUris("127.0.0.1:18001/callback");
}
在实际开发中我们会将这些数据储存于DB,当后端认证通过以后执行这个方法的时候我们动态的查询数据库,将信息写入,类似权限添加部分;
这就完成了;
认证:
当我们访问
http://127.0.0.1:18001/oauth/authorize?client_id=
employed&response_type=code
client_id写上自己的appid,type=code;
- 当我们访问的时候如果没有登录会提示我们登录;当我们登录成功后就会提示我们是否愿意让上面我们配置的
appid
的客户端获取我们的数据信息
- 当点击同意获取的时候,页面就会重定向到我们指定的回调函数,并且后面附带一个参数,授权码;
- 这时候我们后台就能通过回调地址获取到授权码,根据授权码生成Token
- 根据授权码获取acccessToken;访问:
http://127.0.0.1:18001/oauth/token?code=
授权码&grant_type=authorization_code&redirect_url=http://127.0.0.1:18001/callback&scope=all
对应我们配置中的每一条配置; - 这时候又会让我们登录,但是这时候是需要用
appid的账户和密码
进行登录,并不是用户
;意思就是我们商家客户端
拿到了用户的授权
以后,用授权码换取token,这时候需要我们商家
亮明身份换取token
; - 实际中就是我们拿到
用户授权码
,以商家
的身份换取用户
的token
;注意需要post
方法,认证方法选择basic
,提供账号密码进行请求;就能获取到token
{
"access_token": "9455a88a-f6c7-4916-bf51-4d5c054153c4",
"token_type": "bearer",
"expires_in": 43050,
"scope": "all"
}
- 从
oauth服务器
拿到了token我们就可以去调用在我们这个中心注册了的商家的接口
;
小结:大致思路就是当
1.第三方
软件请求联合登录的时候,会拉起主应用
,
2.主应用
向服务器申请一个特殊的token[
未登录提示登录],
3.然后token
交给第三方应用
,
4.第三方应用
拿着token
就可以向主应用的服务器
请求用户资源
;[第三方应用未登录也要提示登录,也就是提供合作时我们分发的appid
和密钥
]
资源服务保护的配置:
在我们需要保护资源的服务中创建一个资源服务配置类ResurceConifg
继承ResourceServerConfigurerAdapter
类;
注入两个参数,appid以及密钥;后续需要db查询
,配置了这个的服务在被请求时必须带上签发的正确token才行;
@Configuration//启用配置
@EnableResourceServer//启用资源服务
public class ResurceConifg extends ResourceServerConfigurerAdapter {
private String appid = "employed";//这里后续需要从数据库中查询遍历用下面的方法填入内存
private String appSecret = "employed_test_security";//这里后续需要从数据库中查询遍历用下面的方法填入内存
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Primary
@Bean
public RemoteTokenServices remoteTokenServices() {
//这里后续需要从数据库中查询遍历用下面的方法填入内存
final RemoteTokenServices tokenServices = new RemoteTokenServices();
//设置授权服务器check_token的端点完整地址
tokenServices.setCheckTokenEndpointUrl("http://127.0.0.1:18001/oauth/check_token");
//设置客户端id与secret,这里的secret不能使用加密!
tokenServices.setClientId(appid);
tokenServices.setClientSecret(appSecret);
return tokenServices;
}
@Override
public void configure(HttpSecurity http) throws Exception {
//设置创建session策略.设置请求后;
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED);
//@formatter:off
//设置所有请求都必须授权
http.authorizeRequests()
.anyRequest().authenticated();
//@formatter:on
}
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
//设置保护的资源,这个资源就是注册在我们oauth服务器内存中个各个商家的资源id,这个服务中要配置好这个才能保护;
resources.resourceId("employed_resource").stateless(true);
}
}
http://127.0.0.1:18081/oauth/check_token?token=14d20998-864f-4eb7-9ca8-af1c86fbc1da
总结:
- 先向
已登录
用户索取同意操作,用户同意之后会以用户的身份颁布一个授权码; 第三方平台
拿着授权码
,向授权服务器申请token;- 第三方平台使用
accessToken
就可以向授权服务器管辖的
用户个人资源服务器
请求数据,获取用户信息 - 第三方平台就可以根据用户信息进行登录或者注册操作;
- 补充:资源发布的时候会给自己指定一个资源id,而第三方进行accsesToken申请的时候也会指定一个资源id(数组),所以只能访问与之匹配的对应资源
误区:
之前一直以为是向授权服务器拿到token以后就可以用token访问第三方的资源了,正觉得奇怪,岂不是每次token都需要授权服务器来解密?
你要是头铁这么做也能用 ,原来资源服务器并不是代表着第三方平台
,而是代表着受开放平台管理
的用户资源
;
我开始还以为资源服务器就是第三方,第三方token经过授权服务器的验证后就可以请求第三方的接口;昨天测试的时候直接就把这个token放到业务代码里去了
感谢这个老哥的图解,一下就明白了,碰巧查资源id的时候看到的图
Spring Security OAuth2之resource_id配置与验证
想法:
由于一开始的不理解,所以也想了一些替代方法,这边记录一下,说不定会派上用场,第三方可以直接使用token进行替代用户登录,只是登录以后另外颁发一个用户的专属token然后就不用授权服务器的token了。。token失效以后再重新获取;