1、Oauth 是一个关于受权(authorization)的开网络标准(规范)前端
OAuth2: 解决的是不一样的企业之间的登陆,本质是受权redis
要能访问各类资源重点是要获取令牌(token),但根据令牌的获取方式不一样,又会有四种受权方式spring
受权码:这是最经常使用的一种方式,指的是第三方应用先申请一个受权码,而后再用该码获取令牌,项目中用的就是这种后端
隐藏式:容许直接向前端颁发令牌。这种方式没有受权码这个中间步骤,因此称为(受权码)"隐藏式"(implicit),通常应用于纯前端项目跨域
密码式:直接经过用户名和密码的方式申请令牌,这方式是最不安全的方式浏览器
凭证式:这种方式的令牌是针对第三方应用,而不是针对用户的,既某个第三方应用的全部用户共用一个令牌,通常用于没有前端的命令行应用tomcat
受权码受权流程:安全
第一步,A 网站提供一个连接,用户点击后就会跳转到 B 网站(权限验证系统)服务器
http://b.com/oauth/authorize?cookie
response_type=code&
client_id=CLIENT_ID&
redirect_uri=CALLBACK_URL&
scope=read
第二步,用户跳转后,B 网站若是没有登陆会要求用户登陆,而后询问是否赞成给予 A 网站受权。用户表示赞成,这时 B 网站就会跳回redirect_uri参数指定的网址,并附加受权码code
http://a.com/callback?code=AUTHORIZATION_CODE
第三步,A 网站拿到受权码之后,在后端,向 B 网站请求令牌。
http://b.com/oauth/token?
client_id=CLIENT_ID&
client_secret=CLIENT_SECRET&
grant_type=authorization_code&
code=AUTHORIZATION_CODE&
redirect_uri=CALLBACK_URL
上面 URL 中,client_id参数和client_secret参数用来让 B 确认 A 的身份(client_secret参数是保密的,所以只能在后端发请求),grant_type参数的值是AUTHORIZATION_CODE,表示采用的受权方式是受权码,code参数是上一步拿到的受权码,redirect_uri参数是令牌颁发后的回调网址。
第四步,B 网站收到请求之后,就会颁发令牌。具体作法是向redirect_uri指定的网址,发送一段 JSON 数据。
{
"access_token":"ACCESS_TOKEN",
"info":{...}
}
接下来用户就能够根据这个access_token来进行访问了,
如A网站拿着token,申请获取用户信息,B网站确认令牌无误,赞成向A网站开放资源。
2、单点: 是解决企业内部的一系列产品登陆问题,安全信任度要比oauth2高
(一)session-cookie机制
一、session-cookie机制出现的根源, http链接是无状态的链接
-------- 同一浏览器向服务端发送屡次请求,服务器没法识别,哪些请求是同一个浏览器发出的
二、为了标识哪些请求是属于同一我的 ---------- 须要在请求里加一个标识参数
方法1-----------直接在url里加一个标识参数(对前端开发有侵入性),如: token
方法2-----------http请求时,自动携带浏览器的cookie(对前端开发无知觉),如:jsessionid=XXXXXXX
三、浏览器标识在网络上的传输,是明文的,不安全的
-----------安全措施:改https来保障
四、cookie的使用限制---依赖域名
-------------- 顶级域名下cookie,会被二级如下的域名请求,自动携带
-------------- 二级域名的cookie,不能携带被其它域名下的请求携带
五、在服务器后台,经过解读标识信息(token或jsessionid),来对应会话是哪一个session
--------------- 一个tomcat,被1000个用户登录,tomcat里必定有1000个session -------》存储格式map《sessionid,session对象》
--------------- 经过前端传递的jsessionid,来对应取的session ------ 动做发生时机request.getsession
(二)session共享方式,实现的单点登录
一、多个应用共用同一个顶级域名,sessionid被种在顶级域名的cookie里
二、后台session经过redis实现共享(重写httprequest、httpsession 或使用springsession框架),即每一个tomcat都在请求开始时,到redis查询session;在请求返回时,将自身session对象存入redis
三、当请求到达服务器时,服务器直接解读cookie中的sessionid,而后经过sessionid到redis中查找到对应会话session对象
四、后台判断请求是否已登录,主要校验session对象中,是否存在登录用户信息
五、整个校验过程,经过filter过滤器来拦截切入,以下图:
六、登录成功时,后台须要给页面种cookie方法以下:
response里,反映的种cookie效果以下:
七、为了request.getsession时,自动能拿到redis中共享的session,
咱们须要重写request的getsession方法(使用HttpServletRequestWrapper包装原request)
(三)cas单点登录方案
一、对于彻底不一样域名的系统,cookie是没法跨域名共享的
二、cas方案,直接启用一个专业的用来登录的域名(好比:cas.com)来供全部的系统登录。
三、当业务系统(如b.com)被打开时,借助cas系统来登录,过程以下:
cas登录的全过程:
(1)、b.com打开时,发现本身未登录 ----》 因而跳转到cas.com去登录
(2)、cas.com登录页面被打开,用户输入账户/密码登录成功
(3)、cas.com登录成功,种cookie到cas.com域名下 -----------》把sessionid放入后台redis《ticket,sesssionid》---页面跳回b.com
String ticket = UUID.randomUUID().toString(); redisTemplate.opsForValue().set(ticket,request.getSession().getId(),20, TimeUnit.SECONDS);//必定要设置过时时间 CookieBasedSession.onNewSession(request,response); response.sendRedirect(user.getBackurl()+"?ticket="+ticket);
(4)、b.com从新被打开,发现仍然是未登录,可是有了一个ticket值
(5)、b.com用ticket值,到redis里查到sessionid,并作session同步 ------ 》种cookie给本身,页面原地重跳
(6)、b.com打开本身页面,此时有了cookie,后台校验登录状态,成功
(7)整个过程交互,列图以下:
四、cas.com的登录页面被打开时,若是此时cas.com原本就是登录状态的,则自动返回生成ticket给业务系统
整个单点登录的关键部位,是利用cas.com的cookie保持cas.com是登录状态,此后任何第三个系统跳入,都将自动完成登录过程
5,本示例中,使用了redis来作cas的服务接口,请根据工做状况,自行替换为合适的服务接口(主要是根据sessionid来判断用户是否已登录)
6,为提升安全性,ticket应该使用过即做废(本例中,会用有效期机制)
public void doFilter(ServletRequest servletRequest,ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) servletRequest; MyRequestWrapper myRequestWrapper = new MyRequestWrapper(request,redisTemplate); //若是未登录状态,进入下面逻辑 String requestUrl = request.getServletPath(); if (!"/toLogin".equals(requestUrl) && !requestUrl.startsWith("/login") && !myRequestWrapper.isLogin()) { /** * ticket为空,或无对应sessionid为空 * --- 代表不是自动登录请求--直接强制到登录页面 */ String ticket = request.getParameter("ticket"); if (null == ticket || null == redisTemplate.opsForValue().get(ticket)){ HttpServletResponse response = (HttpServletResponse)servletResponse; response.sendRedirect("http://cas.com:8090/toLogin?url="+request.getRequestURL().toString()); return ; } /** * 是自动登录请求,则种cookie值进去---本次请求是302重定向 * 重定向后的下次请求,自带本cookie,将直接是登录状态 */ myRequestWrapper.setSessionId((String) redisTemplate.opsForValue().get(ticket)); myRequestWrapper.createSession(); //种cookie CookieBasedSession.onNewSession(myRequestWrapper,(HttpServletResponse)servletResponse); //重定向自流转一次,原地跳转重向一次 HttpServletResponse response = (HttpServletResponse)servletResponse; response.sendRedirect(request.getRequestURL().toString()); return; } try { filterChain.doFilter(myRequestWrapper,servletResponse); } finally { myRequestWrapper.commitSession(); } }
public static void onNewSession(HttpServletRequest request, HttpServletResponse response) { HttpSession session = request.getSession(); String sessionId = session.getId(); Cookie cookie = new Cookie(COOKIE_NAME_SESSION, sessionId); cookie.setHttpOnly(true); cookie.setPath(request.getContextPath() + "/"); cookie.setMaxAge(Integer.MAX_VALUE); response.addCookie(cookie); }