![e6afc2566d2812f00f34835f4e0f2ca8.png](https://i-blog.csdnimg.cn/blog_migrate/93aa36432dfe8db892852d4992954a50.jpeg)
一OAuth2.0授权服务端的设计
在上一篇文章中,我介绍了OAuth2.0协议的基本概念以及作为一个第三方应用在请求授权服务端的时候需要做哪些事情。通过上一篇文章中调用百度OAuth服务的例子我们可以得知,使用授权码模式完成OAuth2.0授权的过程需要以下三个步骤:
- client请求授权服务端,获取Authorization Code;
- client通过Authorization Code再次请求授权服务端,获取Access Token;
- client通过服务端返回的Access Token获取用户的基本信息。
因此,OAuth2.0授权服务端的设计也就主要围绕这几个接口展开,其主要流程是这样的:
![0773b0fd60a9d4a9c1bfc4bf3a0cc664.png](https://i-blog.csdnimg.cn/blog_migrate/faf302a5942fe9f34fed08a7802bb488.jpeg)
明白了整个运行流程,那剩下就好办了。接下来我们需要做的是数据库的表结构设计。
数据库的表结构设计
提示:我在下面只介绍一些表的主要字段,这个Demo中使用的完整的表结构可以参考:gitee.com/zifangsky/O…
(1)auth_client_details:
接入的第三方客户端详情表。这就跟我们要想使用百度OAuth服务就需要事先在百度开发者中心新建一个应用是一个道理,每个想要接入OAuth2.0授权服务的第三方客户端都需要事先在服务端这里“备案”,所以主要需要以下几个字段:
- client_id:每个客户端的client_id是唯一的,通常是一个随机生成的字符串
- client_name:客户端的名称
- client_secret:这个秘钥是客户端和OAuth2.0服务端共同持有,用于鉴别请求中的身份,通常也是一个随机生成的字符串
(2)auth_scope:
用户信息范围表。OAuth2.0服务端在授权第三方客户端访问用户的信息的时候,通常会把用户的信息划分为几个级别,比如用户的基本信息,用户密码、购物记录等高保密性信息。这样划分主要是让用户自主选择把自己哪种信息授权给第三方客户端访问,所以主要需要以下字段:
- scope_name:范围名称
(3)auth_access_token:
Access Token信息表。这个表主要体现出哪个用户授予哪个client何种访问范围的令牌,以及这个令牌的结束日期是哪天。所以主要需要以下几个字段:
- access_token:Access Token字段
- user_id:表明是哪个用户授予的权限
- client_id:表明授予给哪个客户端
- expires_in:过期时间戳,表明这个Token在哪一天过期
- scope:表明可以访问何种范围
(4)auth_refresh_token:
Refresh Token信息表。这个表主要用来记录Refresh Token,在设计表结构的时候需要关联它对应的auth_access_token表的记录。所以主要需要以下几个字段:
- refresh_token:Refresh Token字段
- token_id:它对应的auth_access_token表的记录
- expires_in:过期时间戳
(5)auth_client_user:
用户对某个接入客户端的授权信息表。这个表用于记录client、scope、用户之间的关联关系。所以主要需要以下几个字段:
- auth_client_id:授权对应的auth_client_details表的记录
- user_id:授权对应的user表的记录
- auth_scope_id:授权对应的auth_scope表的记录
明白了授权的整个流程,以及设计好后面需要用到的表结构,那么我们最后就剩下具体代码实现了。
二 OAuth2.0授权服务端主要接口的代码实现
这个Demo的授权服务端的完整可用源码可以参考:gitee.com/zifangsky/O…
(1)客户端注册接口:
某个第三方客户端需要事先在服务端这里“备案”。在这个Demo中我没有写具体的页面,只提供了一个注册接口,其中client_id和client_secret都是随机生成的字符串。
接口地址:http://127.0.0.1:7000/oauth2.0/clientRegister
参数:
{"clientName":"测试客户端","redirectUri":"http://localhost:7080/login","description":"这是一个测试客户端服务"}
![7010b753ecfa5e5046d347d5d128c7b4.png](https://i-blog.csdnimg.cn/blog_migrate/dfb5d062a1b83a03051111d461eaecf1.jpeg)
(2)授权页面:
如果用户之前没有给请求的client授权过,那么在第一次请求Authorization Code的时候会打开授权页面,然后用户手动选择是否授权:
![150bada9767713c73ca1d3b33e71aa39.png](https://i-blog.csdnimg.cn/blog_migrate/a2ac586c18c588fae4d73932d1c6107d.jpeg)
实现代码很简单,就是在用户选择“授权”后,往表auth_client_user插入一条记录。这里就不多说了,可以自行参考一下示例源码。
(3)获取Authorization Code:
根据请求的client_id和scope生成一个字符串——Authorization Code,同时需要将本次请求的授权范围和所属的用户信息保存到Redis中(因为后面在请求Access Token的时候是从第三方客户端的后台直接请求,属于一个新的会话,所以需要提前存一下用户信息)。
接口地址:http://127.0.0.1:7000/oauth2.0/authorize?client_id=7Ugj6XWmTDpyYp8M8njG3hqx&scope=basic&response_type=code&state=AB1357&redirect_uri=http://192.168.197.130:7080/login
/** * 获取Authorization Code * @author zifangsky * @date 2018/8/6 17:40 * @since 1.0.0 * @param request HttpServletRequest * @return org.springframework.web.servlet.ModelAndView */@RequestMapping("/authorize")public ModelAndView authorize(HttpServletRequest request){HttpSession session = request.getSession();User user = (User) session.getAttribute(Constants.SESSION_USER);//客户端IDString clientIdStr = request.getParameter("client_id");//权限范围String scopeStr = request.getParameter("scope");//回调URLString redirectUri = request.getParameter("redirect_uri");//status,用于防止CSRF攻击(非必填)String status = request.getParameter("status");//生成Authorization CodeString authorizationCode = authorizationService.createAuthorizationCode(clientIdStr, scopeStr, user);String params = "?code=" + authorizationCode;if(StringUtils.isNoneBlank(status)){params = params + "&status=" + status;}return new ModelAndView("redirect:" + redirectUri + params);}
调用的cn/zifangsky/service/impl/AuthorizationServiceIm