本文所介绍的项目是一个基于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到自己的项目里阅读==> 项目代码
用户是如何被拦截认证的
@EnableWebSecurity
- 重写 WebSecurityConfigurerAdapter 的 configure(HttpSecurity http) 方法
- 指定哪里请求url是不需要拦截直接放行的
- 指定哪些请求是需要拦截校验的
- 重写 WebSecurityConfigurerAdapter 的 configure(AuthenticationManagerBuilder auth) 方法
- 指定处理认证的service服务类为 MyUserDetailsService
- 比对输入的密码和数据库中的用户密码是否一致
- 自定类 MyUserDetailsService 实现了 spring security的UserDetailsService 接口
- 重写 loadUserByUsername(String username) 方法, 其中username是页面传的属性和值,属性是固定的
- 获取用户权限表对应的权限详情,并把内容设置到 UserDetails.Authorities属性中
- 返回 UserDetails的子类 User
用户注册自动分配角色权限
用户注册则往用户表插入数据,同时往用户角色表也插入一份数据。
当然也可以设计的更复杂些,比如根据来源、时间、白名单、内部推荐等设置不同的权限,读者可自行扩展。
用户只能访问自己所拥有的角色权限访问路径
- 取出当前用户的所有权限
- 取出权限的权限标识字段,并包装成一个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"
}
复制代码
原文
https://juejin.im/post/5da4452c518825647c513ba9