初衷&缘起
最近在构思一件事情,使用spring-boot + spring-security+oauth实现授权的demo;
要测试,动手敲代码最实际,但是,思考也是不能少的,否则也只是复制粘贴代码。
测试oauth的第一步,肯定是先弄明白怎么请求授权服务器,怎么拿到code再拿到token。
所以第一步是创建一个能使用的“client”。通过对比,微博开放平台会相对简单点,因为微信有环境的限制。
在阅读微博开放平台的登录授权接口时,发现要填写一个授权的回调url。
那么疑问就来了:
如果我的“client”应用是一个类似公众号的
比如:有多个菜单,点击跳到不同的页面,而这些页面都需要先登录微博授权
限制:用户点击登录授权之后,回调的url是同一个
结果:那不管点击那个菜单,最后都是跳到回调的页面,多个菜单就形同虚设了
请求过程分析
请注意,这里不是分析如何授权登录,而是授权成功时,如何跳到所点击的入口(菜单)
假设(静默授权,就是不需要用户点击确认):
- 我们的应用(client)的域名是:web.client.com
- client中有一个菜单是:web.client.com/ha
- client中有个回调地址:web.client.com/receive
- 微博开发平台的域名是:oauth.server.com
- 微博确认授权接口地址是:oauth.server.com/test
过程:
- 访问client的任意菜单(web.client.com/ha)
- client发现需要登录微博授权,将用户重定向到oauth.server.com/test
- 微博确认了用户信息,把用户重定向到web.client.com/receive,并带上code
- client接收到code,并且可以通过code拿到token并暂存。
问题在于:最后一步,拿到code再拿到token后,如何跳到我们所点击的入口(菜单)
猜测&实验:session能否实现?
猜测思路
session存放我们所点击的入口(菜单),在接收code的回调接口中,在把用户重定向到我们所点击的入口菜单。
实验代码
host配置两个站点
127.0.0.1 oauth.server.com
127.0.0.1 web.client.com
web.client.com接口
package com.lgh.demo.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
@Controller
public class IndexController {
@RequestMapping("/ha")
public String index(HttpServletRequest request, HttpServletResponse response, HttpSession session) throws Exception {
System.out.println("/ha 请求,redirect:http://oauth.server.com/test");
System.out.println(session.getId());
System.out.println(request.getRequestURL());
session.setAttribute("entrance", request.getRequestURL());
return "redirect:http://oauth.server.com/test";
}
@RequestMapping("ha2")
public String index2(HttpServletRequest request, HttpServletResponse response, HttpSession session) throws Exception {
System.out.println("/ha2 请求,redirect:http://oauth.server.com/test");
System.out.println(session.getId());
System.out.println(request.getRequestURL());
session.setAttribute("entrance", request.getRequestURL());
return "redirect:http://oauth.server.com/test";
}
@RequestMapping("/receive")
public String receive(HttpServletRequest request, HttpServletResponse response, HttpSession session) {
System.out.println("/receive请求, 返回haha.jsp");
System.out.println(session.getId());
System.out.println("code = " + request.getParameter("code"));
System.out.println("入口地址:" + session.getAttribute("entrance"));
return "haha";
}
}
oauth.server.com接口(php)
<?php
session_start();
$code = rand(10000, 99999);
header("Location: http://web.client.com:8080/receive?code=$code");
exit;
?>
实验结果
第一次:web.client.com:8080/ha
第二次:web.client.com:8080/ha2
注意在测试两个接口的中间,要彻底关闭浏览器,否则拿到的session是没变的
再来思考下session的过期和时效
- 过期:一般会有默认的过期时间,是由服务器的默认配置的。
- 失效:一般都是浏览会话结束时失效(浏览会话结束是指:浏览器彻底关掉所有的tab,一个不留,有一个没关,浏览会话都没结束)
脑残测试
条件
- 把client的两个接口都设置session的过期时间:
session.setMaxInactiveInterval(2);
- server接口中,添加睡眠代码:
sleep(10);
结果
由于session都过期了,server才返回code,此时拿到的session自然没有东西啦
延伸思考:
多次访问不同入口
如果真的是多个菜单的场景,会存在一种情况:我点了一个菜单,退出来,再点第二个菜单,session存的入口菜单就会覆盖,这种情况会不会有问题?
分析:
不同时刻关闭重新点击菜单,造成的影响会有点点不一样
如果我们在拿到token之后,在session里存放用户的标志字段,在其他入口地址根据是否存在用户的标志字段来判断是否需要重新认证,就没问题了。