述
本文开始将构建一个 OAuth 服务提供商, 主要就是两大块, 认证服务器和资源服务器, 之后的代码都写在 app 的项目里,所以先把demo项目中的 web 的依赖改成 app 的依赖1
2
3
4
5<dependency>
<groupId>com.security</groupId>
<artifactId>security-example-app</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
先把项目启动一下, 修改一些启动过程中可能会出现的问题
问题一
1 | *************************** |
这个就是验证码的处理器 ValidateCodeFilter
需要一个 AuthenticationFailureHandler
就是认证失败处理器
我们之前的自定义的认证成功失败处理器都是放在 web 项目里面的,因为 app 和 web 的请求成功失败可能不一样,所以先复制一份到 app 项目中来,之后再做修改
复制完成之后再次启动
问题二
1 | *************************** |
少了一个 PasswordEncoder
, 这个之前也是写在了 web 项目里面,这个是通用的,所以挪到core项目里面 SecurityConfig
中 :
1 | @Configuration |
再次启动就ok了
认证服务器实现
app 项目中,新建一个类 MyAuthorizationServerConfig
, 代码如下:1
2
3
4@Configuration
@EnableAuthorizationServer
public class MyAuthorizationServerConfig {
}
这就ok了, 一个注解就实现了
重启项目, 这注意一下控制台的打印, 重点如下:
首先是分配了一个默认的 clientId
和 secret
,因为我们没有配置,所以每次启动的时候会为我们自动生成一个
下面图中,就是加了一些关于认证的请求
这里的 clientId
和 secret
可以自己做一下配置, 以免每次重启项目都要去修改, 在 application.yml
中,如下:1
2
3
4
5security:
oauth2:
client:
client-id: testAppid
client-secret: testSecret
几种授权模式的实现
Spring Security OAuth 是实现的标准的 OAuth2 协议,所以认证请求的参数什么的,在 OAuth2 的官网上可以找到, 传送门
授权码模式
最常用的授权模式,之前我们接QQ微信的时候就是授权码模式
第一步,获取 code 授权码
请求地址1
http://localhost:8080/oauth/authorize?response_type=code&client_id=testAppid&redirect_uri=http://www.example.com&scope=all
这里有几个参数:
response_type
: 固定是codeclient_id
: 上面配置的client-idredirect_uri
: 回调地址scope
: 作用域
请求,如下图:
这里可以看到,要输入用户名和密码,这里输入用户名和密码, 最终会交给我们的 MyUserDetailsService
去验证
输入用户名密码,点击登录,如下:
这里返回一个 403 ,没有权限,原因是 用户必须有一个 ROLE_USER
这样的角色,在 MyUserDetailsService
中,返回用户信息的时候加一下:1
2
3
4
5
6
7
8return new org.springframework.security.core.userdetails.User(
user.getId().toString(),
user.getPassword(),
user.getEnable(),
true,
true,
!user.getLocked(),
AuthorityUtils.commaSeparatedStringToAuthorityList("admin,ROLE_USER"));
重启项目,再次请求授权码的页面, 如下:
这里就是个用户授权页了, 有同意和拒绝,点击同意,如下:
这里会跳转到回调地址,然后携带了code参数, 之后就可以拿code去交换token了
换取 token 的请求地址是 POST /oauth/token
,这里用 postMan 或者其他工具去测试, 请求如下:
这里是需要填写我们配置的 client-id
和 client-secret
这里的参数也是固定的
grant_type
: 固定 authorization_code ,表示授权码模式client_id
: 配置的clientIdcode
: 前面获取到的授权码redirect_uri
: 回调地址,和获取授权码时候传的地址是一样的scope
: 作用域
点击请求,返回如下:1
2
3
4
5
6
7{
"access_token": "8fac95e3-8f50-498d-9dd5-449517e5ff5c",
"token_type": "bearer",
"refresh_token": "182f9e9a-fabf-46f1-94a0-780519239045",
"expires_in": 43199,
"scope": "all"
}
密码模式
和授权码模式的请求地址是一样的, 参数有些不同, 如下:
这里的 grant_type
是 password
表示是密码模式,然后传的参数是 username
和 password
, 就是服务提供商的用户名和密码,这里的返回值也和上面的是一样的
注意
看一下我这里的返回的token,两种模式的请求,返回token是一样的, 在固定的时间段内, 不管请求多少次,返回的token都是相同的
剩余的两种模式不常用,就不演示了
资源服务器的实现
和认证服务器一样, 也是一个注解就实现, 新建类 MyResourcesServerConfig
1
2
3
4
5@Configuration
@EnableResourceServer
public class MyResourcesServerConfig {
}
重启项目,随便访问一个接口,效果如下:
这里是因为我们没有把token传过去
重新获取一下token, 然后放在请求头中, 格式是: Authorization : token_type access_token
,如图:
token 传过去之后就可以访问接口了
这里要注意,现在的token生成是放在内存中的, 所以每次重启项目之后都要重新请求一下
总结
熟悉常用两种授权模式的流程,以及传递的参数等, 获取到token之后,通过请求头传递