基于session和token的身份认证方案

1、基于session的身份认证方案html

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。

 

2、基于token的身份认证方案安全

优势:服务器

  1.token方案保证了服务的无状态,全部的信息都是存在分布式缓存中。基于分布式存储,这样能够水平扩展来支持高并发。
  2.不依赖与cookie机制,能够经过客户端约定的协议来传输token,浏览器能够存在cookie中,手机端能够放在内存或者本地文件中,代价是增长了外部存储依赖以及在代码方面要复杂。
 
注:
Session 是一种HTTP存储机制,目的是为无状态的HTTP提供的持久机制。所谓 Session 认证只是简单的把 User 信息存储到 Session 里,由于 SID 的不可预测性,暂且认为是安全的。这是一种认证手段。 
 Token,指的是 OAuth Token 或相似的机制的话,提供的是 认证 和 受权 ,认证是针对用户,受权是针对 App 。其目的是让 某App 有权利访问 某用户 的信息。
这里的 Token 是惟一的。不能够转移到其它 App 上,也不能够转到其它 用户 上。 
 
下一篇将详细介绍基于JWT的身份认证方案
相关文章
相关标签/搜索