登录,会话管理

100 阅读3分钟

1.HTTP cookie session

cookie

服务器通过response带给浏览器,浏览器下次请求再次访问相同服务器时会带上。浏览器是存内存。所以关闭浏览器就会消失(除非设置存活时间)

cookie两个重要参数

1.作用路径(不可能全部路径都带上cookie)

2.设置存活时间

cookie存客户端明显不安全

// cookie示例

@RequestMapping(path = "/cookie/set", method = RequestMethod.GET)
@ResponseBody
public String setCookie(HttpServletResponse response) {
    // 创建cookie
    Cookie cookie = new Cookie("code", CommunityUtil.generateUUID());
    // 设置cookie生效的范围
    cookie.setPath("/community/alpha");
    // 设置cookie的生存时间
    cookie.setMaxAge(60 * 10);
    // 发送cookie
    response.addCookie(cookie);

    return "set cookie";
}

@RequestMapping(path = "/cookie/get", method = RequestMethod.GET)
@ResponseBody
//这个注解能让我们找到对应cookie,而不是通过遍历获得
public String getCookie(@CookieValue("code") String code) {
    System.out.println(code);
    return "get cookie";
}

session

session的实现用到了cookie。为了分辨哪个浏览器对应哪个session。在设置session时,会给浏览器自动发送cookie,value为sessionid,找到唯一的session。安全可以存敏感数据

缺点:存在客户端内存中,会好卡。且在分布式部署时会出现session失效的问题(下面解释为什么失效)

// session示例

@RequestMapping(path = "/session/set", method = RequestMethod.GET)
@ResponseBody
public String setSession(HttpSession session) {
    //自动给浏览器发送cookie
    session.setAttribute("id", 1);
    session.setAttribute("name", "Test");
    return "set session";
}

@RequestMapping(path = "/session/get", method = RequestMethod.GET)
@ResponseBody
public String getSession(HttpSession session) {
    System.out.println(session.getAttribute("id"));
    System.out.println(session.getAttribute("name"));
    return "get session";
}

`(K1DDZUXQCD@YW%{W02}ZW.png

分布式部署session失效

下面是分布式部署的样子。我们加了负载,导致浏览器带的cookie只能找到其中一个服务器的session,其他的都可能还没生成。

解决:

1.nginx策略设置为同一ip只能访问同一服务器。(这样性能太差)

2.所有服务器同步session,太占用内存

3.在设置一个专门存session的服务器,所有session都到那里去找(如果挂了,所有session失效)

4.敏感数据存数据库。数据库集群很成熟了。但是硬盘读取数据较慢。

5.存非关系型数据库,redis最佳解决方案。

{@KX(R53HBL}ZYTQU}WCI.png

2.验证码生成

生成的png返回前浏览器的方法

@RequestMapping(path = "/kaptcha", method = RequestMethod.GET)
public void getKaptcha(HttpServletResponse response, HttpSession session){
    //由于我们这次返回的是图片,不是string,html所以void,response自己写

    //1.调用bean生成验证码
    String text = kaptchaProdecer.createText();
    BufferedImage image = kaptchaProdecer.createImage(text);

    //2.将验证码存入session
    session.setAttribute("kaptcha",text);

    //3.将图片输出给浏览器.返回图片,且是png类型的图片
    response.setContentType("image/png");
    try {
        //springmvc管理的流,我们不用关
        OutputStream outputStream = response.getOutputStream();
        ImageIO.write(image,"png",outputStream);
    } catch (IOException e) {
        logger.error("验证码获取异常:"+e.getMessage());
    }
}

3.登录登出实现

{@KX(R53HBL}ZYTQU}WCI.png

@RequestMapping(path = "/login",method = RequestMethod.POST)
public String login(String username,String password,String code,boolean remember,
                    Model model,HttpSession session,HttpServletResponse response){
    String kaptcha = (String) session.getAttribute("kaptcha");
    //检查验证码(给表现层处理)
    if (StringUtils.isBlank(kaptcha) || StringUtils.isBlank(code) || !kaptcha.equalsIgnoreCase(code)){
        model.addAttribute("codeMsg","验证码不正确");
        return "/site/login";
    }
    //检查账号,密码。给业务层处理
    //是否勾选记住我
    int expiredSeconds = remember ? REMEMBER_EXPIRED_SECONDS : DEFAULT_EXPIRED_SECONDS;
    Map<String, Object> map = userService.login(username, password, expiredSeconds);
    if (map.containsKey("ticket")){
        //成功,重定向到首页。把凭证当做cookie发给浏览器
        Cookie cookie = new Cookie("ticket",map.get("ticket").toString());
        cookie.setPath(contextPath);
        cookie.setMaxAge(expiredSeconds);
        response.addCookie(cookie);
        return "redirect:/index";
    }else{
        model.addAttribute("usernameMsg",map.get("usernameMsg"));
        model.addAttribute("passwordMsg",map.get("passwordMsg"));
        return "/site/login";
    }
}

@RequestMapping(path = "/logout",method = RequestMethod.GET)
public String logout(@CookieValue("ticket") String ticket){
    //把凭证状态改为1,禁用。并且过期时间改成点击登出的时间,就是立马过期
    userService.logout(ticket);
    //重定向默认发的就是get请求
    return "redirect:/login";
}