分布式Session共享和单点登录实现

本文详细介绍了分布式环境中Session的共享和单点登录(SSO)的实现。通过使用Redis存储Session信息来解决分布式系统中Session不一致的问题,并探讨了基于CAS的单点登录流程,包括登录验证、Ticket票据的生成与验证,以及在不同服务间实现登录状态的同步。
摘要由CSDN通过智能技术生成


基于Session的身份认证

简介

对于一个Web应用,客户端每次请求时,服务器都会打开一个新的会话,而且服务器不会维护客户端的上下文信息,因此如何管理客户端会话是必须要解决的问题。我们知道HTTP是无状态的协议,所以它提供了一种机制,通过Session来保存上下文信息,为每个用户分配一个sessionId,并且每个用户收到的sessionId都不一样,变量的值保存在服务器端。Session是以cookie或URL重写为基础的,默认使用cookie来实现,系统会创造一个名为JSESSIONID的值输出到cookie中。当用户从客户端向服务端发起HTTP请求时,会携带有sessionId的cookie请求, 这样服务端就能根据sessionId进行区分用户了。
在这里插入图片描述

实现

只需要简单定义一个Filter,进行拦截非登录请求,然后确认当前请求的Session中是否能够拿到用户信息,如果能拿到用户信息,那么就是登录状态,否则,认定当前请求无效,将请求转发到登录页面即可

//定义登录过滤器
public class LoginFilter implements Filter {
   
    @Override
    public void doFilter(ServletRequest servletRequest,
                         ServletResponse servletResponse, FilterChain filterChain)
            throws IOException, ServletException {
   
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse)servletResponse;
        Object user = request.getSession().getAttribute(CommonConstants.USER_ATTR);;

        String requestUrl = request.getServletPath();
        //非登陆页面并且不是登陆状态
        if (!requestUrl.startsWith("/login")&& null == user) {
   
            //则拒绝当前请求,请求转发到登陆页面            
            request.getRequestDispatcher("/login").forward(request,response);
            return ;
        }
        filterChain.doFilter(request,servletResponse);
    }

    @Override
    public void destroy() {
   
    }
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
   
    }
}

之后通过@Bean方式注入一个FilterRegistrationBean实例,并为它设置Filter属性,指定自定义的Filter实例,这样自定义的Filter才能在程序中生效

@Configuration
public class WebMvcConfig {
   

    //将过滤器添加到请求中
    @Bean
    public FilterRegistrationBean sessionFilterRegistration() {
   
        FilterRegistrationBean registration = new FilterRegistrationBean();
        registration.setFilter(new LoginFilter());
        registration.addUrlPatterns("/*");
        registration.addInitParameter("paramName", "paramValue");
        registration.setName("loginFilter");
        registration.setOrder(1);
        return registration;
    }
}

分布式Session共享

简介

随着分布式架构的演进,单个服务器已经不能满足系统的需要了,通常都会把系统部署在多台服务器上,通过Nginx负载均衡把请求分发到其中的一台服务器上,这样很可能同一个用户的请求被分发到不同的服务器上,因为Session是保存在服务器上的,那么很有可能第一次请求访问的服务器A,创建了Session,但是第二次访问到了服务器B,这时就会出现取不到Session的情况。因此要在集群环境下使用,最好的解决办法就是使用Session共享

实现

整个实现的核心点就是通过定义一个Request请求对象的Wrapper包装类,负责对当前Request请求的Session获取逻辑进行重写,将Session信息交由Redis进行存储和管理,包括从Redis获取Session信息以及认证成功后将Session信息提交到Redis中。因为实现比较简单,就不分析了,具体实现可以看下面代码的注释:

//request请求的包装类
public class RedisRequestWrapper extends HttpServletRequestWrapper {
   
    private volatile boolean committed = false;
    private String uuid = UUID.randomUUID().toString();

    private RedisSession session;
    private RedisTemplate redisTemplate;

    public RedisRequestWrapper(HttpServletRequest request,RedisTemplate redisTemplate) {
   
        super(request);
        this.redisTemplate = redisTemplate;
    }

    /**
     * 提交session信息到redis
     */
    public void commitSession() {
   
        //避免请求重复提交session
        if (committed) {
   
            return;
        }
        committed = true;
        RedisSession session = this.getSession();
        if (session != null && null != session.getAttrs()) {
   
            //将session信息存入redis
            redisTemplate.opsForHash().putAll(session.getId(),session.getAttrs());
        }
    }

    /**
     * 创建新session
     */
    public RedisSession createSession() {
   
        //从cookie中获得JSESSIONID
        String sessionId = CookieUtil.getRequestedSessionId(this);
        Map<String,Object> attr ;
        if 
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值