最近在学习springboot,session这个点一直困扰了我好久,今天把这些天踩的坑分享出来吧,希望能帮助更多的人。

一、pom.xml配置 

<dependency>
    <groupid>org.springframework.boot</groupid>
    <artifactid>spring-boot-starter-data-redis</artifactid>
</dependency>
 
<dependency>
    <groupid>org.springframework.session</groupid>
    <artifactid>spring-session-data-redis</artifactid>
</dependency>

二、application.properties的redis配置

#redis
spring.redis.host=127.0.0.1
spring.redis.port=6379
spring.redis.password=123456
spring.redis.pool.max-idle=8
spring.redis.pool.min-idle=0
spring.redis.pool.max-active=8
spring.redis.pool.max-wait=-1
#超时一定要大于0
spring.redis.timeout=3000
spring.session.store-type=redis

在配置redis时需要确保redis安装正确,并且配置notify-keyspace-events egx,spring.redis.timeout设置为大于0,我当时这里配置为0时springboot时启不起来。

三、编写登录状态拦截器redissessioninterceptor

//拦截登录失效的请求
public class redissessioninterceptor implements handlerinterceptor
{
    @autowired
    private stringredistemplate redistemplate;
 
    @override
    public boolean prehandle(httpservletrequest request, httpservletresponse response, object handler) throws exception
    {
        //无论访问的地址是不是正确的,都进行登录验证,登录成功后的访问再进行分发,404的访问自然会进入到错误控制器中
        httpsession session = request.getsession();
        if (session.getattribute("loginuserid") != null)
        {
            try
            {
                //验证当前请求的session是否是已登录的session
                string loginsessionid = redistemplate.opsforvalue().get("loginuser:" + (long) session.getattribute("loginuserid"));
                if (loginsessionid != null && loginsessionid.equals(session.getid()))
                {
                    return true;
                }
            }
            catch (exception e)
            {
                e.printstacktrace();
            }
        }
 
        response401(response);
        return false;
    }
 
    private void response401(httpservletresponse response)
    {
        response.setcharacterencoding("utf-8");
        response.setcontenttype("application/json; charset=utf-8");
 
        try
        {
            response.getwriter().print(json.tojsonstring(new returndata(statuscode.need_login, "", "用户未登录!")));
        }
        catch (ioexception e)
        {
            e.printstacktrace();
        }
    }
 
    @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
    {
 
    }
}

四、配置拦截器

@configuration
public class websecurityconfig extends webmvcconfigureradapter
{
    @bean
    public redissessioninterceptor getsessioninterceptor()
    {
        return new redissessioninterceptor();
    }
 
    @override
    public void addinterceptors(interceptorregistry registry)
    {
        //所有已api开头的访问都要进入redissessioninterceptor拦截器进行登录验证,并排除login接口(全路径)。必须写成链式,分别设置的话会创建多个拦截器。
        //必须写成getsessioninterceptor(),否则sessioninterceptor中的@autowired会无效
        registry.addinterceptor(getsessioninterceptor()).addpathpatterns("/api/**").excludepathpatterns("/api/user/login");
        super.addinterceptors(registry);
    }
}

五、登录控制器

@restcontroller
@requestmapping(value = "/api/user")
public class logincontroller
{
    @autowired
    private userservice userservice;
 
    @autowired
    private stringredistemplate redistemplate;
 
    @requestmapping("/login")
    public returndata login(httpservletrequest request, string account, string password)
    {
        user user = userservice.finduserbyaccountandpassword(account, password);
        if (user != null)
        {
            httpsession session = request.getsession();
            session.setattribute("loginuserid", user.getuserid());
            redistemplate.opsforvalue().set("loginuser:" + user.getuserid(), session.getid());
 
            return new returndata(statuscode.request_success, user, "登录成功!");
        }
        else
        {
            throw new myexception(statuscode.account_or_password_error, "账户名或密码错误!");
        }
    }
 
    @requestmapping(value = "/getuserinfo")
    public returndata get(long userid)
    {
        user user = userservice.finduserbyuserid(userid);
        if (user != null)
        {
            return new returndata(statuscode.request_success, user, "查询成功!");
        }
        else
        {
            throw new myexception(statuscode.user_not_exist, "用户不存在!");
        }
    }
}

六、效果

我在浏览器上登录,然后获取用户信息,再在postman上登录相同的账号,浏览器再获取用户信息,就会提示401错误了,浏览器需要重新登录才能获取得到用户信息,同样,postman上登录的账号就失效了。

浏览器:

postman:

七、核心原理详解

分布式session需要解决两个难点:1、正确配置redis让springboot把session托管到redis服务器。2、唯一登录。

1、redis:

redis需要能正确启动到出现如下效果才证明redis正常配置并启动

同时还要保证配置正确

@enablecaching
@enableredishttpsession(maxinactiveintervalinseconds = 30)//session过期时间(秒)
@configuration
public class redissessionconfig
{
    @bean
    public static configureredisaction configureredisaction()
    {
        //让springsession不再执行config命令
        return configureredisaction.no_op;
    }
}

springboot启动后能在redis上查到缓存的session才能说明整个redis+springboot配置成功!

2、唯一登录:

1、用户登录时,在redis中记录该userid对应的sessionid,并将userid保存到session中。

httpsession session = request.getsession();
session.setattribute("loginuserid", user.getuserid());
redistemplate.opsforvalue().set("loginuser:" + user.getuserid(), session.getid());

2、访问接口时,会在redissessioninterceptor拦截器中的prehandle()中捕获,然后根据该请求发起者的session中保存的userid去redis查当前已登录的sessionid,若查到的sessionid与访问者的sessionid相等,那么说明请求合法,放行。否则抛出401异常给全局异常捕获器去返回给客户端401状态。

唯一登录经过我的验证后满足需求,暂时没有出现问题,也希望大家能看看有没有问题,有的话给我点好的建议!

到此这篇关于springboot+springsession+redis实现session共享及唯一登录示例的文章就介绍到这了,更多相关springboot 唯一登录内容请搜索www.887551.com以前的文章或继续浏览下面的相关文章希望大家以后多多支持www.887551.com!