OAuth二、CAS单点登陆

1、Oauth 是一个关于受权(authorization)的开网络标准(规范)前端

OAuth2: 解决的是不一样的企业之间的登陆,本质是受权redis

要能访问各类资源重点是要获取令牌(token),但根据令牌的获取方式不一样,又会有四种受权方式spring

  1. 受权码(authorization-code)
  2. 隐藏式(implicit)
  3. 密码式(password)
  4. 客户端凭证(client credentials)

受权码这是最经常使用的一种方式,指的是第三方应用先申请一个受权码,而后再用该码获取令牌,项目中用的就是这种后端

隐藏式:容许直接向前端颁发令牌。这种方式没有受权码这个中间步骤,因此称为(受权码)"隐藏式"(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);
}
相关文章
相关标签/搜索