登录验证基础cookie-session-token

原理篇

前言:

HTTP是无状态的协议。浏览器先后两次发起请求,服务器不会知道你是同一个人。

但是除去静态的资源库,几乎所有的web应用都需要记录身份。

Cookie:

cookie是存在于浏览器中的一种<K,V>数据结构。cookie有两个关键参数,Age/Path(Domain+Path)。这两个参数决定了存活时间以及访问什么URL会携带cookie

cookie实现验证的过程如下:

  1. 初次登录,request携带username/password
  2. 服务器验证成功,response.addCookie(new Cookie(key,value));
  3. 后续访问,拦截器request.getCookies()。获取健为key的value值~~后续操作

第二步中,如果说验证了用户名密码之后,直接new Cookie("user",username)。拦截器直接获取key="user"的cookie,这其实已经做到了登录验证,不过这个风险太大了。基本没有用

如果保险一点,比如我生成一个UUID,new Cookie("uuid",uuid)。同时将这个UUID存入数据库,table(user,uuid),在拦截器中可以通过select user from talbe where uuid=#{uuid}的方式

Session:

cookie中说的保险一点的方式,其实就是Session的思想。

session实现验证的过程如下:

  1. 初次登录,request携带username/password
  2. 服务器验证成功,request.getSession()    session.addAttribute("user",name)
  3. 后续访问,拦截器request.getSession()  session.getAttribute("user")~~~后续操作

这里没有出现cookie,不过实际仍然用到了cookie。cookie中存放了会话相关的sessionId,下次请求时request.getSession()直接会根据cookie中的sessionId获取

但这个和上面Cookie方式中存入数据库也有区别,这个session信息是存放在内存中的(其实,cookie也可以设计成这种方式),而不是数据库。

Token:

为什么要采用session? 简言之,为了证明你是你。

session的做法是,你初次访问的时候给你取个名字,你自己记住这个名字,服务器也会记住你的名字。后续访问的时候只要服务器中有你的名字就行。

token的做法是,你初次访问的时候根据你的指纹给你取个复杂的名字,并把这个名字给你,但是服务器不会记住你的名字。后续访问的时候再次根据你的指纹获取你的复杂的名字。如果你带来的名字和新生成的名字一样,那验证通过。——即加密

token实现验证的过程如下:

  1. 初次登录,request携带username/password
  2. 服务器验证成功,token=tokenMethod(username)  response.addCookie("token",token).addCookie("user",username)
  3. 后续访问,拦截器获取cookies   realToken=tokenMethod(username)   对比token和realToken~~后续操作

 

 

Demo篇

Cookie

采用cookie+数据库实现登录认证,每一个登录的用户都会将登录信息存入数据库

验证用户/密码等略

@Override
public void addInterceptors(InterceptorRegistry registry){
    registry.addInterceptor(new HandlerInterceptor(){
        @Override
        public boolean preHandle (HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {
            //根据request获取cookie与当前会话中的ticket比对
            String t= CookieUtils.getCookie("t",request);
            if(t!=null){
                Ticket ticket=ticketService.getTicket(t);  //数据库中有则代表已经登录
                if(ticket!=null){
                    if(ticket.getExpiredAt().compareTo(new Date())>0){  //登录过期判断
                        User u=userService.getUser(ticket.getUserId());
                        ConcurrentUtils.setHost(u);
                    }
                }
            }
            return true;
        }
    }).addPathPatterns("/**");

Session

session有一个问题。如果说我们的应用做了多实例+负载均衡,用户登录访问了A实例,那在A实例中确实有用户的session,可B实例是没有的。下次用户访问到了B实例,携带了sessionId可是B实例并不认识这个Id

@RestController
public class LoginController {

    Map<String,String> userlist=new HashMap<>();
    {
       for(int i=0;i<5;i++)
           userlist.put("user"+i,"user"+i);
    };

    @RequestMapping("login")
    public String login(@RequestParam("name")String name, @RequestParam("pwd")String pwd, HttpServletRequest request){
        if(userlist.containsKey(name))
            if(userlist.get(name).equals(pwd)){
                HttpSession session=request.getSession();
                session.setAttribute("whoLogin",name);
                return "Welcome You: "+name;
            }
            else return "Wrong Pwd";
        return "Invalid account";
    }

    @RequestMapping("check")
    public String check(){
        return "If you can see me, you already logged in.";
    }
}

//拦截器设置
registry.addInterceptor(new HandlerInterceptor(){
    @Override
    public boolean preHandle (HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {
        //根据request获取cookie与当前会话中的ticket比对
        HttpSession session=request.getSession();
        String who=(String)session.getAttribute("whoLogin");
        if(who==null||who.equals(""))
            response.sendError(6666);
        return true;
    }
}).addPathPatterns("/login/check");

Token

由于使用token验证的服务器端并没有保存sessionid等信息,所以即便是在多实例中也不会出现这边登录了那边‘未登录’的情况。不过这样,每次的请求都会做一个加密,会消耗服务器资源

@RestController
@RequestMapping("/token/")
public class TokenController {

    Map<String,String> userList=new HashMap<>();
    {
        for(int i=0;i<5;i++)
            userList.put("user"+i,"user"+i);
    }
    @RequestMapping("login")
    public String login(HttpServletRequest request, HttpServletResponse response) throws IOException {
        String name=request.getParameter("name");
        String pwd=request.getParameter("pwd");
        if(name==null||pwd==null)
            response.sendError(6666,"Please enter username OR password.");

        if(userList.containsKey(name)){
            if(userList.get(name).equals(pwd)){
                String token= DigestUtils.md5DigestAsHex(name.getBytes());
                Cookie cookie=new Cookie("token",token);
                response.addCookie(cookie);
                cookie=new Cookie("username",name);
                response.addCookie(cookie);
                return "Welcome You, "+name;
            }else
                return "Wrong Password";
        }
        return "Invalid Account";
    }

    @RequestMapping("check")
    public String check(){
        return "If you can see me, you already logged in.";
    }
}


//拦截器配置
registry.addInterceptor(new HandlerInterceptor(){
    @Override
    public boolean preHandle (HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {
        String name=null,token=null;
        for(Cookie cookie : request.getCookies()){
            if(cookie.getName().equals("username"))
                name=cookie.getValue();
            if(cookie.getName().equals("token"))
                token=cookie.getValue();
        }
        if(name==null||token==null){
            response.sendError(6666,"Please login first");
            return false;
        }
        String realToken= DigestUtils.md5DigestAsHex(name.getBytes());
        if(!token.equals(realToken)){
            response.sendError(6666,"Invalid Token");
            return false;
        }
        return true;
    }
}).addPathPatterns("/token/check");

 

参考:

牛客网项目:https://git.nowcoder.com/11000160/BookManager   用的是cookie+数据库验证方式

原理分析:https://www.cnblogs.com/moyand/p/9047978.html    session/token  Demo是根据这个文章的思想写出的

Demo没啥技术含量,如果有新手记不住API可以参考

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值