zoukankan      html  css  js  c++  java
  • 基于session和token的身份认证方案

    一、基于session的身份认证方案

    1.方案图示

    2.比较通用的鉴权流程实现如下:

    在整个流程中有两个拦截器。
    第一个拦截器AuthInteceptor是为了每一次的请求的时候都先去session中取user对象,如果session中有,就放user对象到threadlocal中。这是为了业务处理的时候能直接获取用户对象。
    第二个拦截器AuthActionInteceptor是先看页面是否需要登录,如果不需要登录,则直接进行业务逻辑处理;如果需要登录,则判断是否已经登录(UserContext.getUser()是否为空),没有登录则重定向到登录页面,登录后是会把用户信息放到session当中的 。 
    @Component
    public class AuthInterceptor implements HandlerInterceptor{
    
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
                throws Exception {
            
            Map<String, String[]> map = request.getParameterMap();
            map.forEach((k,v) -> {
                if (k.equals("errorMsg") || k.equals("successMsg") || k.equals("target")) {
                    request.setAttribute(k, Joiner.on(",").join(v));
                }
            });
            String reqUri =    request.getRequestURI();
            if (reqUri.startsWith("/static") || reqUri.startsWith("/error") ) {
                return true;
            }
            HttpSession session = request.getSession(true);//参数为true,没有session则创建新的
            User user =  (User)session.getAttribute(CommonConstants.USER_ATTRIBUTE);
            if (user != null) {
                UserContext.setUser(user);
            }
            return true;
        }
    
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
                ModelAndView modelAndView) throws Exception {
            
        }
    
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
                throws Exception {
            UserContext.remove();
        }
        
    
    }
    View Code
    @Component
    public class AuthActionInterceptor implements HandlerInterceptor {
    
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
                throws Exception {
            User user = UserContext.getUser();
            if (user == null) {
                String msg = URLEncoder.encode("请先登录","utf-8");
                String target = URLEncoder.encode(request.getRequestURL().toString(),"utf-8");
                if ("GET".equalsIgnoreCase(request.getMethod())) {
                    response.sendRedirect("/accounts/signin?errorMsg=" + msg + "&target="+target);
                    return false;//修复bug,未登录要返回false
                }else {
                    response.sendRedirect("/accounts/signin?errorMsg="+msg);
                    return false;//修复bug,未登录要返回false
                }
            }
            return true;
        }
    
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
                ModelAndView modelAndView) throws Exception {
    
        }
    
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
                throws Exception {
    
        }
    
    }
    View Code
    @Configuration
    public class WebMvcConf extends WebMvcConfigurerAdapter {
    
        @Autowired
        private AuthActionInterceptor authActionInterceptor;
        
        @Autowired
        private AuthInterceptor authInterceptor;
        
        @Override
        public void addInterceptors(InterceptorRegistry registry){
             registry.addInterceptor(authInterceptor).addPathPatterns("/**").excludePathPatterns("/static");
                registry
                    .addInterceptor(authActionInterceptor).addPathPatterns("/house/toAdd")
                    .addPathPatterns("/accounts/profile").addPathPatterns("/accounts/profileSubmit")
                    .addPathPatterns("/house/bookmarked").addPathPatterns("/house/del")
                    .addPathPatterns("/house/ownlist").addPathPatterns("/house/add")
                    .addPathPatterns("/house/toAdd").addPathPatterns("/agency/agentMsg")
                    .addPathPatterns("/comment/leaveComment").addPathPatterns("/comment/leaveBlogComment");
                super.addInterceptors(registry);
        }
    
        
    }
    View Code
    为什么要分成两个拦截器呢?
    第一个拦截器是为了拦截除静态资源以外的任何资源,并且从session中取user放到ThreadLocal中,至于为什么要这么做,是可以在后面的业务处理逻辑中都可以方便使用到它,具体请参考ThreadLocal管理session
    第二个拦截器才是对于需要进行访问控制的资源进行判断处理。
    最后业务逻辑处理完后,都需要将user从threadlocal中移除,以免影响以后的请求,因为我们的请求线程是处在线程池中的,每个线程在下一个请求中都可以复用。
     
    3.缺点
      1).sessio需要存在服务器内存中,这样就不能跨实例共享。当下一次请求被分发到另一个实例的时候,就会造成重新登录。
      2).在高并发情况下,session放在内存中受限于内存的大小
      3).session依赖于浏览器的cookie机制,对于移动客户端就很难支持。移动端使用token。

    二、基于token的身份认证方案

    优点:

      1.token方案保证了服务的无状态,所有的信息都是存在分布式缓存中。基于分布式存储,这样可以水平扩展来支持高并发。
      2.不依赖与cookie机制,可以通过客户端约定的协议来传输token,浏览器可以存在cookie中,手机端可以放在内存或者本地文件中,代价是增加了外部存储依赖以及在代码方面要复杂。
     
    注:
    Session 是一种HTTP存储机制,目的是为无状态的HTTP提供的持久机制。所谓 Session 认证只是简单的把 User 信息存储到 Session 里,因为 SID 的不可预测性,暂且认为是安全的。这是一种认证手段。 
     Token,指的是 OAuth Token 或类似的机制的话,提供的是 认证 和 授权 ,认证是针对用户,授权是针对 App 。其目的是让 某App 有权利访问 某用户 的信息。
    这里的 Token 是唯一的。不可以转移到其它 App 上,也不可以转到其它 用户 上。 
     
    下一篇将详细介绍基于JWT的身份认证方案
  • 相关阅读:
    POJ2355 Railway tickets DP优化
    POJ3280 Cheapest Palindrome 区间DP
    POJ2352 Stars 线段树
    适牛的类岛娘头文件<转载>
    Ural 1519 Formula 1 插头DP(单回路)
    POJ3345 Bribing FIPA 树形DP+分组背包
    6个变态的C语言HelloWorld程序<转载>
    POJ2374 Fence Obstacle Course DP+线段树优化
    POJ3133 Manhattan Wiring 插头DP
    ACdream 完美数 数位DP
  • 原文地址:https://www.cnblogs.com/xiangkejin/p/9011007.html
Copyright © 2011-2022 走看看