ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

分布式会话

2022-04-25 10:32:47  阅读:130  来源: 互联网

标签:String 登录 用户 会话 tmpTicket cookie com 分布式


一、什么是会话   会话Session代表的是客户端与服务器的一次交互过程,这个过程可以是连续也可以是时断时续的。曾经的Servlet时代(jsp),一旦用户与服务端交互,服务器 用户创建一个session,同时前端会有一个jsessionid,每次交互都会携带。如此一来,服务器只要在接到用户请求时候,就可以拿到jsessionid,并根据这个ID在 对应的会话session,当拿到session会话后,那么我们就可以操作会话了。会话存活期间,我们就能认为用户一直处于正在使用着网站的状态,一旦session超期 就可以认为用户已经离开网站,停止交互了。用户的身份信息,我们也是通过session来判断的,在session中可以保存不同用户的信息。 session的使用之前在单体部分演示过,代码如下:  
@GetMapping("/setSession") 
public Object setSession(HttpServletRequest request) { 
HttpSession session = request.getSession(); 
session.setAttribute("userInfo", "new user"); 
session.setMaxInactiveInterval(3600); 
session.getAttribute("userInfo"); 
// session.removeAttribute("userInfo"); 
return "ok"; 
}

 

二、无状态会话 HTTP请求是无状态的,用户向服务端发起多个请求,服务端并不会知道这多次请求都是来自同一用户,这个就是无状态的。cookie的出现就是为了有状态的记录 常见的,ios与服务端交互,安卓与服务端交互,前后端分离,小程序与服务端交互,他们都是通过发起http来调用接口数据的,每次交互服务端都不会拿到客户 是我们可以通过手段去处理,比如每次用户发起请求的时候携带一个userid或者user-token,如此一来,就能让服务端根据用户id或token来获得相应的数据。每个 次请求都能被服务端识别来自同一个用户。   三、有状态会话  Tomcat中的会话,就是有状态的,一旦用户和服务端交互,就有会话,会话保存了用户的信息,这样用户就“有状态”了,服务端会和每个客户端都保持着这样的 个由容器来管理(也就是tomcat),这个session会话是保存到内存空间里的,如此一来,当不同的用户访问服务端,那么就能通过会话知道谁是谁了。tomcat会 是为了让http请求变的有状态。如果用户不再和服务端交互,那么会话超时则消失,结束了他的生命周期。如此一来,每个用户其实都会有一个会话被维护,这就 话。 场景:在传统项目或者jsp项目中是使用的最多的session都是有状态的,session的存在就是为了弥补http的无状态。 注:tomcat会话可以通过手段实现多系统之间的状态同步,但是会损耗一定的时间,一旦发生同步那么用户请求就会等待,这种做法不 五、单Tomcat会话(图) 先来看一下单个tomcat会话,这个就是有状态的,用户首次访问服务端,这个时候会话产生,并且会设置jsessionid放入cookie中,后续每次请求都会携带jsessi 户状态。      

 

 

四: 代码实现通过redis进行分布式会话

   1):用户注册,登录,修改用户信息的时候 生成一个token 放入缓存中作为分布式缓存token 同时将用户信息以及token放入cookie中

       

 // 实现用户的redis会话 这里token直接简单生成 正常用该通过username+password+时间戳
        String uniqueToken = UUID.randomUUID().toString().trim();
        redisUtils.set(REDIS_USER_TOKEN + ":" + user.getId(),
                uniqueToken);

        UsersVO usersVO = new UsersVO();
        BeanUtils.copyProperties(user, usersVO);
        usersVO.setUserUniqueToken(uniqueToken);
// 将用户信息存储到cookie中
 CookieUtils.setCookie(request, response, "user",
                JsonUtils.objectToJson(usersVO), true);

   2):用户退出登录删除redis并清楚cookie

 // 清除用户的相关信息的cookie
 CookieUtils.deleteCookie(request, response, "user");

// 用户退出登录,清除redis中user的会话信息
redisOperator.del(REDIS_USER_TOKEN + ":" + userId);

   /**
     * 
     * @Description: 删除Cookie带cookie域名
     * @param request
     * @param response
     * @param cookieName
     */
    public static void deleteCookie(HttpServletRequest request, HttpServletResponse response,
            String cookieName) {
        doSetCookie(request, response, cookieName, null, -1, false);
//        doSetCookie(request, response, cookieName, "", -1, false);
    }

 /**
     * 
     * @Description: 设置Cookie的值,并使其在指定时间内生效
     * @param request
     * @param response
     * @param cookieName
     * @param cookieValue
     * @param cookieMaxage    cookie生效的最大秒数
     * @param isEncode
     */
    private static final void doSetCookie(HttpServletRequest request, HttpServletResponse response,
            String cookieName, String cookieValue, int cookieMaxage, boolean isEncode) {
        try {
            if (cookieValue == null) {
                cookieValue = "";
            } else if (isEncode) {
                cookieValue = URLEncoder.encode(cookieValue, "utf-8");
            }
            Cookie cookie = new Cookie(cookieName, cookieValue);
            if (cookieMaxage > 0)
                cookie.setMaxAge(cookieMaxage);
            if (null != request) {// 设置域名的cookie
                String domainName = getDomainName(request);
                logger.info("========== domainName: {} ==========", domainName);
                if (!"localhost".equals(domainName)) {
                    cookie.setDomain(domainName);
                }
            }
            cookie.setPath("/");
            response.addCookie(cookie);
        } catch (Exception e) {
             e.printStackTrace();
        }
    }

    3):添加请求拦截器 校验是否是合法有效的token访问

package com.imooc.controller.interceptor;

import com.imooc.utils.IMOOCJSONResult;
import com.imooc.utils.JsonUtils;
import com.imooc.utils.RedisOperator;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;

public class UserTokenInterceptor implements HandlerInterceptor {

    @Autowired
    private RedisOperator redisOperator;

    public static final String REDIS_USER_TOKEN = "redis_user_token";

    /**
     * 拦截请求,在访问controller调用之前
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

//        System.out.println("进入到拦截器,被拦截。。。");

        String userId = request.getHeader("headerUserId");
        String userToken = request.getHeader("headerUserToken");

        if (StringUtils.isNotBlank(userId) && StringUtils.isNotBlank(userToken)) {
            String uniqueToken = redisOperator.get(REDIS_USER_TOKEN + ":" + userId);
            if (StringUtils.isBlank(uniqueToken)) {
//                System.out.println("请登录...");
                returnErrorResponse(response, IMOOCJSONResult.errorMsg("请登录..."));
                return false;
            } else {
                if (!uniqueToken.equals(userToken)) {
//                    System.out.println("账号在异地登录...");
                    returnErrorResponse(response, IMOOCJSONResult.errorMsg("账号在异地登录..."));
                    return false;
                }
            }
        } else {
//            System.out.println("请登录...");
            returnErrorResponse(response, IMOOCJSONResult.errorMsg("请登录..."));
            return false;
        }


        /**
         * false: 请求被拦截,被驳回,验证出现问题
         * true: 请求在经过验证校验以后,是OK的,是可以放行的
         */
        return true;
    }

    public void returnErrorResponse(HttpServletResponse response,
                                    IMOOCJSONResult result) {
        OutputStream out = null;
        try {
            response.setCharacterEncoding("utf-8");
            response.setContentType("text/json");
            out = response.getOutputStream();
            out.write(JsonUtils.objectToJson(result).getBytes("utf-8"));
            out.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (out != null) {
                    out.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }

    /**
     * 请求访问controller之后,渲染视图之前
     * @param request
     * @param response
     * @param handler
     * @param modelAndView
     * @throws Exception
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

    }

    /**
     * 请求访问controller之后,渲染视图之后
     * @param request
     * @param response
     * @param handler
     * @param ex
     * @throws Exception
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

    }
}

 

五: 如何实现相同顶级域名的单点登录 SSO

引子 在面试过程中有时候会被问到单点登录,那么单点登录又称之为Single Sign On,简称SSO,单点登录可以通过基于用户会话的共享,他分文两种,先来看第一 的原理是分布式会话来实现。   比如说现在有个一级域名为 www.imooc.com ,是教育类网站,但是慕课网有其他的产品线,可以通过构建二级域名提供服务给用户访问,比如: music.imoo  op.imooc.com , blog.imooc.com 等等,分别为慕课音乐,慕课电商以及慕课博客等,用户只需要在其中一个站点登录,那么其他站点也会随之而登录。 也就是说,用户自始至终只在某一个网站下登录后,那么他所产生的会话,就共享给了其他的网站,实现了单点网站登录后,同时间接登录了其他的网站,那么 单点登录,他们的会话是共享的,都是同一个用户会话。Cookie + Redis 实现 SSO   那么之前我们所实现的分布式会话后端是基于redis的,如此会话可以流窜在后端的任意系统,都能获取到缓存中的用户数据信息,前端通过使用cookie,可以保 一级二级下获取,那么这样一来,cookie中的信息userid和token是可以在发送请求的时候携带上的,这样从前端请求后端后是可以获取拿到的,这样一来,其实 登录注册以后,其实cookie和redis中都会带有用户信息,只要用户不退出,那么就能在任意一个站点实现登录了。 那么这个原理主要也是cookie和网站的依赖关系,顶级域名 www.imooc.com 和 *.imooc.com 的cookie值是可以共享的,可以被携带 比如设置为 .imooc.com , .t.mukewang.com ,如此是OK的。 二级域名自己的独立cookie是不能共享的,不能被其他二级域名获取,比如: music.imooc.com 的cookie是不能被 mtv.imooc.com 共 互不影响,要共享必须设置为 .imooc.com 。  Cookie共享测试 找到前端项目app.js,开启如下代码,设置你的对应域名,需要和SwitchHosts相互对应:  

 

 

六: 通过CAS实现在不同的域名下单点登录 顶级域名不同怎么办? 如果顶级域名都不一样,咋办?比如 www.imooc.com 要和 www.mukewang.com 的会话实现共享,这个时候如下图,这个时候的cookie由于顶级域名不同,就不能实现cookie跨域了,每个站点各自请求到服务端,cookie无法同步。比如,www.imooc.com下的用户发起请求,但是他又访问了www.abc.com,由于cookie无法携带,所以会要你二次登录。    
/**
* userTicket: 用于表示用户在CAS端的一个登录状态:已经登录
* tmpTicket: 用于颁发给用户进行一次性的验证的票据,有时效性
*/

/**
* 举例:
* 我们去动物园玩耍,大门口买了一张统一的门票,这个就是CAS系统的全局门票和用户全局会话。
* 动物园里有一些小的景点,需要凭你的门票去领取一次性的票据,有了这张票据以后就能去一些小的景点游玩了。
* 这样的一个个的小景点其实就是我们这里所对应的一个个的站点。
* 当我们使用完毕这张临时票据以后,就需要销毁。
*/

实现思路:

1: 用户第一次访问,进入/login接口,通过cookie获取全局票据没,没有,跳转统一登录界面

2: 实现登录

输入用户名密码 提交,进入/dologin接口,实现登录 获取用户信息,userId作为key userinfo作为value存储到redis中看/dologin接口实现 生成分布式会话 全部票据 临时票据 全局票据放入cookie中,跳转到verifyTmpTicket接口继续验证

3: 验证

进入verifyTmpTicket接口验证,通过临时票据换取cookie中全局票据,通过全局票据缓存用户信息

4: 用户在此访问另一个系统,系统调用登录接口 ,判断cookie中是否存在全局票据,存在全局票据,那么生成一个临时票据返回前端,前端按照之前的逻辑跳转到验证接口换取了用户信息

这样就实现了不同系统下的单点登录

临时票据的作用: (个人理解) 其实整个逻辑下来完全可以摘除临时票据的,那么为什么会有临时票据呢,个人理解还是为了安全性,因为临时票据是有实效的,当时效过了那么只能在此访问

/login登录接口,从cookie中获取全局票据在此获取临时票据,那么我们可以在这里加上一些黑名单等一些验证,防止一些黑客攻击网站,如果不用临时票据的话,那么只要cookie存在的时候,

黑客可以在这些系统下频繁的访问,切没有超时时间

 

代码: 后端

package com.imooc.controller;

import com.imooc.pojo.Users;
import com.imooc.pojo.vo.UsersVO;
import com.imooc.service.UserService;
import com.imooc.utils.IMOOCJSONResult;
import com.imooc.utils.JsonUtils;
import com.imooc.utils.MD5Utils;
import com.imooc.utils.RedisOperator;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.UUID;

@Controller
public class SSOController {

    @Autowired
    private UserService userService;

    @Autowired
    private RedisOperator redisOperator;

    public static final String REDIS_USER_TOKEN = "redis_user_token";
    public static final String REDIS_USER_TICKET = "redis_user_ticket";
    public static final String REDIS_TMP_TICKET = "redis_tmp_ticket";

    public static final String COOKIE_USER_TICKET = "cookie_user_ticket";

    @GetMapping("/login")
    public String login(String returnUrl,
                        Model model,
                        HttpServletRequest request,
                        HttpServletResponse response) {

        model.addAttribute("returnUrl", returnUrl);

        // 1. 获取userTicket门票,如果cookie中能够获取到,证明用户登录过,此时签发一个一次性的临时票据并且回跳
        String userTicket = getCookie(request, COOKIE_USER_TICKET);

        boolean isVerified = verifyUserTicket(userTicket);
        if (isVerified) {
            String tmpTicket = createTmpTicket();
            return "redirect:" + returnUrl + "?tmpTicket=" + tmpTicket;
        }

        // 2. 用户从未登录过,第一次进入则跳转到CAS的统一登录页面
        return "login";
    }

    /**
     * 校验CAS全局用户门票
     * @param userTicket
     * @return
     */
    private boolean verifyUserTicket(String userTicket) {

        // 0. 验证CAS门票不能为空
        if (StringUtils.isBlank(userTicket)) {
            return false;
        }

        // 1. 验证CAS门票是否有效
        String userId = redisOperator.get(REDIS_USER_TICKET + ":" + userTicket);
        if (StringUtils.isBlank(userId)) {
            return false;
        }

        // 2. 验证门票对应的user会话是否存在
        String userRedis = redisOperator.get(REDIS_USER_TOKEN + ":" + userId);
        if (StringUtils.isBlank(userRedis)) {
            return false;
        }

        return true;
    }

    /**
     * CAS的统一登录接口
     *      目的:
     *          1. 登录后创建用户的全局会话                 ->  uniqueToken
     *          2. 创建用户全局门票,用以表示在CAS端是否登录  ->  userTicket
     *          3. 创建用户的临时票据,用于回跳回传          ->  tmpTicket
     */
    @PostMapping("/doLogin")
    public String doLogin(String username,
                          String password,
                          String returnUrl,
                          Model model,
                          HttpServletRequest request,
                          HttpServletResponse response) throws Exception {

        model.addAttribute("returnUrl", returnUrl);

        // 0. 判断用户名和密码必须不为空
        if (StringUtils.isBlank(username) ||
                StringUtils.isBlank(password)) {
            model.addAttribute("errmsg", "用户名或密码不能为空");
            return "login";
        }

        // 1. 实现登录
        Users userResult = userService.queryUserForLogin(username,
                MD5Utils.getMD5Str(password));
        if (userResult == null) {
            model.addAttribute("errmsg", "用户名或密码不正确");
            return "login";
        }

        // 2. 实现用户的redis会话
        String uniqueToken = UUID.randomUUID().toString().trim();
        UsersVO usersVO = new UsersVO();
        BeanUtils.copyProperties(userResult, usersVO);
        usersVO.setUserUniqueToken(uniqueToken);
        redisOperator.set(REDIS_USER_TOKEN + ":" + userResult.getId(),
                JsonUtils.objectToJson(usersVO));

        // 3. 生成ticket门票,全局门票,代表用户在CAS端登录过
        String userTicket = UUID.randomUUID().toString().trim();

        // 3.1 用户全局门票需要放入CAS端的cookie中
        setCookie(COOKIE_USER_TICKET, userTicket, response);

        // 4. userTicket关联用户id,并且放入到redis中,代表这个用户有门票了,可以在各个景区游玩
        redisOperator.set(REDIS_USER_TICKET + ":" + userTicket, userResult.getId());

        // 5. 生成临时票据,回跳到调用端网站,是由CAS端所签发的一个一次性的临时ticket
        String tmpTicket = createTmpTicket();

        /**
         * userTicket: 用于表示用户在CAS端的一个登录状态:已经登录
         * tmpTicket: 用于颁发给用户进行一次性的验证的票据,有时效性
         */

        /**
         * 举例:
         *      我们去动物园玩耍,大门口买了一张统一的门票,这个就是CAS系统的全局门票和用户全局会话。
         *      动物园里有一些小的景点,需要凭你的门票去领取一次性的票据,有了这张票据以后就能去一些小的景点游玩了。
         *      这样的一个个的小景点其实就是我们这里所对应的一个个的站点。
         *      当我们使用完毕这张临时票据以后,就需要销毁。
         */

//        return "login";
        return "redirect:" + returnUrl + "?tmpTicket=" + tmpTicket;
    }


    @PostMapping("/verifyTmpTicket")
    @ResponseBody
    public IMOOCJSONResult verifyTmpTicket(String tmpTicket,
                        HttpServletRequest request,
                        HttpServletResponse response) throws Exception {

        // 使用一次性临时票据来验证用户是否登录,如果登录过,把用户会话信息返回给站点
        // 使用完毕后,需要销毁临时票据
        String tmpTicketValue = redisOperator.get(REDIS_TMP_TICKET + ":" + tmpTicket);
        if (StringUtils.isBlank(tmpTicketValue)) {
            return IMOOCJSONResult.errorUserTicket("用户票据异常");
        }

        // 0. 如果临时票据OK,则需要销毁,并且拿到CAS端cookie中的全局userTicket,以此再获取用户会话
        if (!tmpTicketValue.equals(MD5Utils.getMD5Str(tmpTicket))) {
            return IMOOCJSONResult.errorUserTicket("用户票据异常");
        } else {
            // 销毁临时票据
            redisOperator.del(REDIS_TMP_TICKET + ":" + tmpTicket);
        }

        // 1. 验证并且获取用户的userTicket
        String userTicket = getCookie(request, COOKIE_USER_TICKET);
        String userId = redisOperator.get(REDIS_USER_TICKET + ":" + userTicket);
        if (StringUtils.isBlank(userId)) {
            return IMOOCJSONResult.errorUserTicket("用户票据异常");
        }

        // 2. 验证门票对应的user会话是否存在
        String userRedis = redisOperator.get(REDIS_USER_TOKEN + ":" + userId);
        if (StringUtils.isBlank(userRedis)) {
            return IMOOCJSONResult.errorUserTicket("用户票据异常");
        }

        // 验证成功,返回OK,携带用户会话
        return IMOOCJSONResult.ok(JsonUtils.jsonToPojo(userRedis, UsersVO.class));
    }

    @PostMapping("/logout")
    @ResponseBody
    public IMOOCJSONResult logout(String userId,
                               HttpServletRequest request,
                               HttpServletResponse response) throws Exception {

        // 0. 获取CAS中的用户门票
        String userTicket = getCookie(request, COOKIE_USER_TICKET);

        // 1. 清除userTicket票据,redis/cookie
        deleteCookie(COOKIE_USER_TICKET, response);
        redisOperator.del(REDIS_USER_TICKET + ":" + userTicket);

        // 2. 清除用户全局会话(分布式会话)
        redisOperator.del(REDIS_USER_TOKEN + ":" + userId);

        return IMOOCJSONResult.ok();
    }

    /**
     * 创建临时票据
     * @return
     */
    private String createTmpTicket() {
        String tmpTicket = UUID.randomUUID().toString().trim();
        try {
            redisOperator.set(REDIS_TMP_TICKET + ":" + tmpTicket,
                    MD5Utils.getMD5Str(tmpTicket), 600);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return tmpTicket;
    }

    private void setCookie(String key,
                           String val,
                           HttpServletResponse response) {

        Cookie cookie = new Cookie(key, val);
        cookie.setDomain("sso.com");
        cookie.setPath("/");
        response.addCookie(cookie);
    }

    private void deleteCookie(String key,
                           HttpServletResponse response) {

        Cookie cookie = new Cookie(key, null);
        cookie.setDomain("sso.com");
        cookie.setPath("/");
        cookie.setMaxAge(-1);
        response.addCookie(cookie);
    }

    private String getCookie(HttpServletRequest request, String key) {

        Cookie[] cookieList = request.getCookies();
        if (cookieList == null || StringUtils.isBlank(key)) {
            return null;
        }

        String cookieValue = null;
        for (int i = 0 ; i < cookieList.length; i ++) {
            if (cookieList[i].getName().equals(key)) {
                cookieValue = cookieList[i].getValue();
                break;
            }
        }

        return cookieValue;
    }


}

代码: 前端(简易版本,简单看看)

统一登录节界面:

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>SSO单点登录</title>
</head>
<body>
<h1>欢迎访问单点登录系统</h1>
<form action="doLogin" method="post">
    <input type="text" name="username" placeholder="请输入用户名"/>
    <input type="password" name="password" placeholder="请输入密码"/>
    <input type="hidden" name="returnUrl" th:value="${returnUrl}">
    <input type="submit" value="提交登录"/>
</form>
<span style="color:red" th:text="${errmsg}"></span>

</body>
</html>

 

mtv-系统

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0,maximum-scale=1.0, user-scalable=0">

    <title>MTV系统</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.9/dist/vue.js"></script>
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
</head>

<body>
    <div id="mtv">
        <h1>MTV系统</h1>
        <div v-if="userIsLogin != true">
            欢迎陌生人,请<a>登录</a>!
        </div>
        <div v-if="userIsLogin == true">
            欢迎<span style="color: green;">{{userInfo.username}}</span>登录系统!
            <br/>
            <button @click="logout">点我退出登录</button>
        </div>
    </div>
    <script type="text/javascript " src="js/app.js"></script>
    <script type="text/javascript">
        var index = new Vue({
            el: "#mtv",
            data: {
                userIsLogin: false,
                userInfo: {},
            },
            created() {
                var me = this;
                // 通过cookie判断用户是否登录
                this.judgeUserLoginStatus();

                // http://www.mtv.com:8080/sso-mtv/index.html

                // 判断用户是否登录
                var userIsLogin = this.userIsLogin;
                if (!userIsLogin) {
                    // 如果没有登录,判断一下是否存在tmpTicket临时票据
                    var tmpTicket = app.getUrlParam("tmpTicket");
                    console.log("tmpTicket: " + tmpTicket);
                    if (tmpTicket != null && tmpTicket != "" && tmpTicket != undefined) {
                        // 如果有tmpTicket临时票据,就携带临时票据发起请求到cas验证获取用户会话
                        var serverUrl = app.serverUrl;
                        axios.defaults.withCredentials = true;
                        axios.post('http://www.sso.com:8090/verifyTmpTicket?tmpTicket=' + tmpTicket)
                            .then(res => {
                                if (res.data.status == 200) {
                                    var userInfo = res.data.data;
                                    console.log(res.data.data);
                                    this.userInfo = userInfo;
                                    this.userIsLogin = true;
                                    app.setCookie("user",  JSON.stringify(userInfo));
                                    window.location.href = "http://www.mtv.com:8080/sso-mtv/index.html";
                                } else {
                                    alert(res.data.msg);
                                    console.log(res.data.msg);
                                }
                            });
                    } else {
                        // 如果没有tmpTicket临时票据,说明用户从没登录过,那么就可以跳转至cas做统一登录认证了
                        window.location.href = app.SSOServerUrl + "/login?returnUrl=http://www.mtv.com:8080/sso-mtv/index.html";
                    }

                    console.log(app.SSOServerUrl + "/login?returnUrl=" + window.location);
                }
            },
            methods: {
                logout() {
                    var userId = this.userInfo.id;
                    axios.defaults.withCredentials = true;
                    axios.post('http://www.sso.com:8090/logout?userId=' + userId)
                        .then(res => {
                            if (res.data.status == 200) {
                                var userInfo = res.data.data;
                                console.log(res.data.data);
                                this.userInfo = {};
                                this.userIsLogin = false;

                                app.deleteCookie("user");

                                alert("退出成功!");
                                
                            } else {
                                alert(res.data.msg);
                                console.log(res.data.msg);
                            }
                        });
                },
                // 通过cookie判断用户是否登录
                judgeUserLoginStatus() {
                    var userCookie = app.getCookie("user");
                    if (
                        userCookie != null &&
                        userCookie != undefined &&
                        userCookie != ""
                    ) {
                        var userInfoStr = decodeURIComponent(userCookie);
                        // console.log(userInfo);
                        if (
                            userInfoStr != null &&
                            userInfoStr != undefined &&
                            userInfoStr != ""
                        ) {
                            var userInfo = JSON.parse(userInfoStr);
                            // 判断是否是一个对象
                            if ( typeof(userInfo)  == "object" ) {
                                this.userIsLogin = true;
                                // console.log(userInfo);
                                this.userInfo = userInfo;
                            } else {
                                this.userIsLogin = false;
                                this.userInfo = {};
                            }
                        }
                    } else {
                        this.userIsLogin = false;
                        this.userInfo = {};
                    }
                }
            }
        });
    </script>
</body>

</html>

music系统

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0,maximum-scale=1.0, user-scalable=0">

    <title>MUSIC系统</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.9/dist/vue.js"></script>
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
</head>

<body>
    <div id="music">
        <h1>MUSIC系统</h1>
        <div v-if="userIsLogin != true">
            欢迎陌生人,请<a>登录</a>!
        </div>
        <div v-if="userIsLogin == true">
            欢迎<span style="color: green;">{{userInfo.username}}</span>登录系统!
            <br/>
            <button @click="logout">点我退出登录</button>
        </div>
    </div>
    <script type="text/javascript " src="js/app.js"></script>
    <script type="text/javascript">
        var index = new Vue({
            el: "#music",
            data: {
                userIsLogin: false,
                userInfo: {},
            },
            created() {
                var me = this;
                // 通过cookie判断用户是否登录
                this.judgeUserLoginStatus();

                // http://www.music.com:8080/sso-music/index.html

                // 判断用户是否登录
                var userIsLogin = this.userIsLogin;
                if (!userIsLogin) {
                    // 如果没有登录,判断一下是否存在tmpTicket临时票据
                    var tmpTicket = app.getUrlParam("tmpTicket");
                    console.log("tmpTicket: " + tmpTicket);
                    if (tmpTicket != null && tmpTicket != "" && tmpTicket != undefined) {
                        // 如果有tmpTicket临时票据,就携带临时票据发起请求到cas验证获取用户会话
                        axios.defaults.withCredentials = true;
                        axios.post('http://www.sso.com:8090/verifyTmpTicket?tmpTicket=' + tmpTicket)
                            .then(res => {
                                if (res.data.status == 200) {
                                    var userInfo = res.data.data;
                                    console.log(res.data.data);
                                    this.userInfo = userInfo;
                                    this.userIsLogin = true;
                                    app.setCookie("user",  JSON.stringify(userInfo));
                                    window.location.href = "http://www.music.com:8080/sso-music/index.html";
                                } else {
                                    alert(res.data.msg);
                                    console.log(res.data.msg);
                                }
                            });
                    } else {
                        // 如果没有tmpTicket临时票据,说明用户从没登录过,那么就可以跳转至cas做统一登录认证了
                        window.location.href = app.SSOServerUrl + "/login?returnUrl=http://www.music.com:8080/sso-music/index.html";
                    }

                    console.log(app.SSOServerUrl + "/login?returnUrl=" + window.location);
                }
            },
            methods: {
                logout() {
                    var userId = this.userInfo.id;
                    axios.defaults.withCredentials = true;
                    axios.post('http://www.sso.com:8090/logout?userId=' + userId)
                        .then(res => {
                            if (res.data.status == 200) {
                                var userInfo = res.data.data;
                                console.log(res.data.data);
                                this.userInfo = {};
                                this.userIsLogin = false;
                                app.deleteCookie("user");

                                alert("退出成功!");
                            } else {
                                alert(res.data.msg);
                                console.log(res.data.msg);
                            }
                        });
                },
                // 通过cookie判断用户是否登录
                judgeUserLoginStatus() {
                    var userCookie = app.getCookie("user");
                    if (
                        userCookie != null &&
                        userCookie != undefined &&
                        userCookie != ""
                    ) {
                        var userInfoStr = decodeURIComponent(userCookie);
                        // console.log(userInfo);
                        if (
                            userInfoStr != null &&
                            userInfoStr != undefined &&
                            userInfoStr != ""
                        ) {
                            var userInfo = JSON.parse(userInfoStr);
                            // 判断是否是一个对象
                            if ( typeof(userInfo)  == "object" ) {
                                this.userIsLogin = true;
                                // console.log(userInfo);
                                this.userInfo = userInfo;
                            } else {
                                this.userIsLogin = false;
                                this.userInfo = {};
                            }
                        }
                    } else {
                        this.userIsLogin = false;
                        this.userInfo = {};
                    }
                }
            }
        });
    </script>
</body>

</html>

 

标签:String,登录,用户,会话,tmpTicket,cookie,com,分布式
来源: https://www.cnblogs.com/langcangsheng/p/16187360.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有