来自: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

本项目包括功能有

  • 新用户

    1. 用户注册自动分配角色权限
    2. 用户只能访问自己所拥有的角色权限访问路径
  • 开放平台

    1. 用户可以申请获取客户ID和客户密钥
    2. 用户可以通过客户ID获取授权码
    3. 用户可以通过客户ID和密钥以及授权码获取access token 和referrsh token和scope
  • 资源api服务(order-service/open-api-service)

    1. 可自定义配置需授权url
    2. 可自定义配置受限url的访问scope
    3. 未授权用户或访问权限不足用户,页面提示相信息
    4. 用户通过access token 来访问对应url

项目概览

首先来看一下项目的结构图,了解项目的大致布局
【20191116】微服务API开放授权平台的设计与实现 - 图1

  • .idea:idea工具生成的文件
  • docker: 存放Dockerfile文件用于构建容器镜像
  • config: 项目中关于spring security和web的配置
  • controller:请求控制器
  • entity: 对象实体类包
  • handler:逻辑处理类包
  • mapper:存放mybatis mapper接口类
  • service: 业务逻辑处理类
  • util: 通用的工具类
  • postman:postman工具的测试用例
  • sql:项目的初始化sql语句
  • templates: 页面模板文件

接下来正式介绍关于项目的细节,由于项目本身就已经有不少中文注释,所以在讲解的时候会收缩起一些代码的具体实现,如果读者不习惯可以在这里点在线比对阅读或者fork到自己的项目里阅读==> 项目代码

用户是如何被拦截认证的

【20191116】微服务API开放授权平台的设计与实现 - 图2

  1. 自定类 SecurityConfig 继承了 WebSecurityConfigurerAdapter,说明SecurityConfig将会是一个 Http安全配置适配器
  2. 通过 @EnableWebSecurity打开了 HttpSecurity 的安全配置,则该类将生效

【20191116】微服务API开放授权平台的设计与实现 - 图3

  1. 重写 WebSecurityConfigurerAdapter 的 configure(HttpSecurity http) 方法
  2. 指定哪里请求url是不需要拦截直接放行的
  3. 指定哪些请求是需要拦截校验的

【20191116】微服务API开放授权平台的设计与实现 - 图4

  1. 重写 WebSecurityConfigurerAdapter 的 configure(AuthenticationManagerBuilder auth) 方法
  2. 指定处理认证的service服务类为 MyUserDetailsService
  3. 比对输入的密码和数据库中的用户密码是否一致

【20191116】微服务API开放授权平台的设计与实现 - 图5

  1. 自定类 MyUserDetailsService 实现了 spring security的UserDetailsService 接口
  2. 重写 loadUserByUsername(String username) 方法, 其中username是页面传的属性和值,属性是固定的
  3. 获取用户权限表对应的权限详情,并把内容设置到 UserDetails.Authorities属性中
  4. 返回 UserDetails的子类 User

    用户注册自动分配角色权限

    用户注册则往用户表插入数据,同时往用户角色表也插入一份数据。
    当然也可以设计的更复杂些,比如根据来源、时间、白名单、内部推荐等设置不同的权限,读者可自行扩展。
    【20191116】微服务API开放授权平台的设计与实现 - 图6

    用户只能访问自己所拥有的角色权限访问路径

    【20191116】微服务API开放授权平台的设计与实现 - 图7

  5. 取出当前用户的所有权限

  6. 取出权限的权限标识字段,并包装成一个List 集合
  7. 保存包装的权限list集合到 UserDetails 对象中

【20191116】微服务API开放授权平台的设计与实现 - 图8

  1. 取出所有权限表中内容
  2. http.authorizeRequests()获取当前的认证对象
  3. 把权限表中的内容全部设置到 authorizeRequests中, antMatchers表示拦截的url, hasAnyAuthority表示可以访问的权限标识
  4. 因为在上图中user已经设置了自己所拥有权限的权限标识,所以可以访问被拦截的url

【20191116】微服务API开放授权平台的设计与实现 - 图9

用户可以申请获取客户ID和客户密钥

首先来看一下表结构,oauth_client_details 为spring cloud oath2自带的表, user_client_secret 为我们自己创建的表
【20191116】微服务API开放授权平台的设计与实现 - 图10 【20191116】微服务API开放授权平台的设计与实现 - 图11

  1. 生成 OauthClientDetails 数据并保存至数据库中
  2. 获取当前登录人信息,并绑定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
    【20191116】微服务API开放授权平台的设计与实现 - 图12 【20191116】微服务API开放授权平台的设计与实现 - 图13

  3. 因为是要获取授权码,response_type=code为固定值

  4. 客户申请的客户ID
  5. 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
    【20191116】微服务API开放授权平台的设计与实现 - 图14

  6. 通过客户ID获取到的授权码

  7. 客户申请的客户ID
  8. 客户申请的客户ID配套的客户秘钥
  9. oauth_client_details 表中的 web_server_redirect_uri
  10. oauth_client_details 表中的 scope 【20191116】微服务API开放授权平台的设计与实现 - 图15
    1. # 出现如下类似错误标识code失效,重新在获取授权码操作即可
    2. {
    3. "error": "invalid_grant",
    4. "error_description": "Invalid authorization code: iq30f9"
    5. }
    6. 复制代码

    商户id和商户秘钥获取accessToken和刷新accessToken

    【20191116】微服务API开放授权平台的设计与实现 - 图16
  • 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

    【20191116】微服务API开放授权平台的设计与实现 - 图17 通过 antMatchers()authenticated() 方法来配置,查看图中红框内的内容, 这些都是代表需要拦截验证的请求路径url,除了拦截请求外,还可以指定拦截请求方式,比如访问 /api/trade/ ,只对 POST 访问做拦截, GET 请求的访问一律放行。

    如何自定义配置受限url的访问scope

    【20191116】微服务API开放授权平台的设计与实现 - 图18 通过 access() 方法指定访问的该 url 需要的 scope,取值为 oauth_client_details 表中的 scope 值, 如果scope值与代码中定义的不一致,则会出现如下错误:【20191116】微服务API开放授权平台的设计与实现 - 图19

    未授权用户或访问权限不足用户,页面提示相应信息

    用户如何通过 access token 来访问对应url

    【20191116】微服务API开放授权平台的设计与实现 - 图20
  1. 配置验证 accessToken 是否有效的服务地址,其实就是上面发放 accessToken 的服务
  2. 需要配置商户id和商户秘钥获取accessToken,用来通过的开发平台的校验。有的小伙伴可能奇怪了,文中上面也有介绍验证accessToken是否有效,但是并没有需要验证登录,这里为什么要呢? 我们来看下源码:

spring-security-oauth2-2.2.1.RELEASE-sources.jar 中的 RemoteTokenServices.java 用于远程调用 token 服务(开发平台)进行操作
【20191116】微服务API开放授权平台的设计与实现 - 图21

  1. 因为给http header 设置了Authorization属性表示需要验证,所以取了上述配置中的 clientId 和 clientSecret,否则发送请求会被开放平台验证失败
  2. postForMap() 包装参数并执行请求发送

    项目代码点这里

    问题

    /oauth/check_token 401

作者:蒋老湿
链接:https://juejin.im/post/5da4452c518825647c513ba9
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。