来自:https://juejin.im/post/5da4452c518825647c513ba9
本文所介绍的项目是一个基于oath2协议的应用,实现的的功能逻辑与QQ互联,微博开放平台类似,都是同一套认证授权流程。
项目结构简单易懂,却不偷工减料,在学习完本文内容后,读者可以直接获取文中的项目代码用于学习或者copy到公司的生产项目中修改后使用,真正达到学以致用的目的。
所涉及技术栈:
- java
- springboot 2.0.1.RELEASE
- spring security 2.0.1.RELEASE
- spring cloud oauth2 2.0.0.M7
- mybatis 2.0.1
本项目包括功能有
新用户
- 用户注册自动分配角色权限
- 用户只能访问自己所拥有的角色权限访问路径
开放平台
- 用户可以申请获取客户ID和客户密钥
- 用户可以通过客户ID获取授权码
- 用户可以通过客户ID和密钥以及授权码获取access token 和referrsh token和scope
资源api服务(order-service/open-api-service)
- 可自定义配置需授权url
- 可自定义配置受限url的访问scope
- 未授权用户或访问权限不足用户,页面提示相信息
- 用户通过access token 来访问对应url
项目概览
首先来看一下项目的结构图,了解项目的大致布局
- .idea:idea工具生成的文件
- docker: 存放Dockerfile文件用于构建容器镜像
- config: 项目中关于spring security和web的配置
- controller:请求控制器
- entity: 对象实体类包
- handler:逻辑处理类包
- mapper:存放mybatis mapper接口类
- service: 业务逻辑处理类
- util: 通用的工具类
- postman:postman工具的测试用例
- sql:项目的初始化sql语句
- templates: 页面模板文件
接下来正式介绍关于项目的细节,由于项目本身就已经有不少中文注释,所以在讲解的时候会收缩起一些代码的具体实现,如果读者不习惯可以在这里点在线比对阅读或者fork到自己的项目里阅读==> 项目代码
用户是如何被拦截认证的
- 自定类 SecurityConfig 继承了 WebSecurityConfigurerAdapter,说明SecurityConfig将会是一个 Http安全配置适配器
- 通过
@EnableWebSecurity
打开了 HttpSecurity 的安全配置,则该类将生效
- 重写 WebSecurityConfigurerAdapter 的 configure(HttpSecurity http) 方法
- 指定哪里请求url是不需要拦截直接放行的
- 指定哪些请求是需要拦截校验的
- 重写 WebSecurityConfigurerAdapter 的 configure(AuthenticationManagerBuilder auth) 方法
- 指定处理认证的service服务类为 MyUserDetailsService
- 比对输入的密码和数据库中的用户密码是否一致
- 自定类 MyUserDetailsService 实现了 spring security的UserDetailsService 接口
- 重写 loadUserByUsername(String username) 方法, 其中username是页面传的属性和值,属性是固定的
- 获取用户权限表对应的权限详情,并把内容设置到 UserDetails.Authorities属性中
-
用户注册自动分配角色权限
用户注册则往用户表插入数据,同时往用户角色表也插入一份数据。
当然也可以设计的更复杂些,比如根据来源、时间、白名单、内部推荐等设置不同的权限,读者可自行扩展。
用户只能访问自己所拥有的角色权限访问路径
取出当前用户的所有权限
- 取出权限的权限标识字段,并包装成一个List 集合
- 保存包装的权限list集合到 UserDetails 对象中
- 取出所有权限表中内容
- http.authorizeRequests()获取当前的认证对象
- 把权限表中的内容全部设置到 authorizeRequests中, antMatchers表示拦截的url, hasAnyAuthority表示可以访问的权限标识
- 因为在上图中user已经设置了自己所拥有权限的权限标识,所以可以访问被拦截的url
用户可以申请获取客户ID和客户密钥
首先来看一下表结构,oauth_client_details 为spring cloud oath2自带的表, user_client_secret 为我们自己创建的表
- 生成 OauthClientDetails 数据并保存至数据库中
获取当前登录人信息,并绑定OauthClientDetails的clientId至user_client_secret表中
用户可以通过客户ID获取授权码
具体的实现在spring-security-oauth包中的, 非本项目内的自我实现
客户ID获取授权码 请求url: http://localhost:8080/oauth/authorize?response_type=code&client_id=client_92&redirect_uri=http://localhost:8080/code
因为是要获取授权码,response_type=code为固定值
- 客户申请的客户ID
oauth_client_details 表中的 web_server_redirect_uri
用授权码获取access_token
具体的实现在spring-security-oauth包中的
org.springframework.security.oauth2.provider.endpoint.TokenEndpoint
类,有兴趣的同学可以在里面进行debug调试
用授权码获取access_token 请求url: http://localhost:8080/oauth/token?grant_type=authorization_code&code=8fGtOV&client_id=client_92&client_secret=123456&redirect_uri=http://localhost:8080/code&scope=all通过客户ID获取到的授权码
- 客户申请的客户ID
- 客户申请的客户ID配套的客户秘钥
- oauth_client_details 表中的 web_server_redirect_uri
- oauth_client_details 表中的 scope
# 出现如下类似错误标识code失效,重新在获取授权码操作即可
{
"error": "invalid_grant",
"error_description": "Invalid authorization code: iq30f9"
}
复制代码
商户id和商户秘钥获取accessToken和刷新accessToken
- grant_type: 授权类型,这里设置为密码模式
- username: 用户名
- password: 用户密码
- client_id: 申请得到的客户Id
- client_secret: 申请得到的客户秘钥
- scope:范围标识,取自 oauth_client_details 表中的 scope
刷新 accessToken
刷新 accessToken的 请求url: http://localhost:8080/oauth/token?grant_type=refresh_token&refresh_token=4741d043-e202-4de0-ae21-4f5c7ec5626e&client_id=client_1&client_secret=123456验证accessToken是否有效
验证accessToken的 请求url: http://localhost:8080/oauth/check_token?token=1131809b-ee12-4aea-9823-ea4454b96f2d
资源api服务
到了这里,我们来看另一个项目 order-service, 其实取名叫 order-api-resource 比较合适,不过也懒得改了。这个项目的结构与代码比较稀少,所以学习起来更为轻松。如何自定义配置需拦截授权的url
通过antMatchers()
和authenticated()
方法来配置,查看图中红框内的内容, 这些都是代表需要拦截验证的请求路径url,除了拦截请求外,还可以指定拦截请求方式,比如访问 /api/trade/ ,只对 POST 访问做拦截, GET 请求的访问一律放行。如何自定义配置受限url的访问scope
通过access()
方法指定访问的该 url 需要的 scope,取值为 oauth_client_details 表中的 scope 值, 如果scope值与代码中定义的不一致,则会出现如下错误:未授权用户或访问权限不足用户,页面提示相应信息
用户如何通过 access token 来访问对应url
- 配置验证 accessToken 是否有效的服务地址,其实就是上面发放 accessToken 的服务
- 需要配置商户id和商户秘钥获取accessToken,用来通过的开发平台的校验。有的小伙伴可能奇怪了,文中上面也有介绍验证accessToken是否有效,但是并没有需要验证登录,这里为什么要呢? 我们来看下源码:
spring-security-oauth2-2.2.1.RELEASE-sources.jar 中的 RemoteTokenServices.java 用于远程调用 token 服务(开发平台)进行操作
- 因为给http header 设置了Authorization属性表示需要验证,所以取了上述配置中的 clientId 和 clientSecret,否则发送请求会被开放平台验证失败
- postForMap() 包装参数并执行请求发送
项目代码点这里
问题
/oauth/check_token 401
作者:蒋老湿
链接:https://juejin.im/post/5da4452c518825647c513ba9
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。