spring Boot整合Spring session

spring Boot整合Spring session

1.模拟场景

有两台相同的服务器,并且用nginx管理,用户的访问在两台机器之间权重相同(也就是说用户的访问是一次一台服务器,下一次访问的是另一台服务器),这样存在的问题是登录在一台服务器,下次访问应该传递回的是登录后的界面,但因为跨服务器,服务器对权限检查发现session中未包含信息,表明没有权限,而做了错误响应
SpringSession就是为了解决上面的问题

2.场景实现

在这里插入图片描述

2.1依赖导入

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

2.2 页面开发

login.html
在这里插入图片描述

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
        <form th:action="@{/dologin}" method="post">
            用户名:<input type="text" name="username"><br>
            密码:<input type="password" name="password"><br>
            <input type="submit" value="提交">
        </form>

    </body>
</html>

home.html
在这里插入图片描述

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
        <h1><span th:text="${#session.getAttribute('username')}"></span>你好,登录成功</h1>
    <form th:action="@{/logout}" method="post">
        <input type="submit" value="退出登录">
    </form>
    </body>
</html>

2.3controller开发

DemoController


@Controller
public class DemoController {
    @GetMapping("/")//访问根地址时进入home界面,但是会被自定义的拦截器先进行拦截判断是否有权限
    public String homeShow(){
        return "home";
    }
    //响应可以的dologin操作,并将username属性存放到session中,要求用户进行重定向到另一台机器
    @PostMapping("/dologin")
    public String doLogin(@RequestParam("username")String userName,
                          @RequestParam("password") String password, HttpServletRequest request){
        request.getSession().setAttribute("username",userName);
        return "redirect:/";
    }
    //响应用户的logout操作
    @PostMapping("/logout")
    public String LoginOut( HttpServletRequest request){
        //登出将session失效
        request.getSession().invalidate();
        return "login";
    }

}

2.4 拦截器开发

LoginInterceptor

@Component //注入到容器中
@Slf4j //用于日志记录
public class LoginInterceptor extends HandlerInterceptorAdapter {
    //定义拦截器,前置拦截,对权限进行判断
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {


        //用日志功能查看拦截到的路径
        log.info("拦截到"+request.getRequestURI());
        //检查路径,对/login  /dologin不拦截
        if (request.getRequestURI().equals("/login")|| request.getRequestURI().equals("/dologin")){
            return true;
        }
        //如果session中存在username,表明已登录则放行
        if (request.getSession()!=null&& request.getSession().getAttribute("username")!=null){
//        String username =(String) request.getSession().getAttribute("username");
//        if ( username.trim().length()>0 ){
            return true;
        }

        //验证没有登录,重定向到登录界面
        response.sendRedirect(request.getContextPath()+"/login");
        return false;
    }
}

2.5 web配置类

WebMvcConfig

@Configuration
@AllArgsConstructor //所有变量的构造方法,利用spring自动注入所有成员变量
public class WebMvcConfig implements WebMvcConfigurer {

    private LoginInterceptor loginInterceptor;
    //添加/login的请求响应界面
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/login");
    }
    //注入拦截器
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginInterceptor);
    }
}

2.6 配置文件

在这里插入图片描述
增加两个配置文件,其中定义端口号分别为8081 和9092
在这里插入图片描述
application.properties设置启动时默认的配置文件为M1
在这里插入图片描述

2.7复制一台服务器

在idea中右键复制一个相同的服务器
在这里插入图片描述
右键新建的机器,进行编辑配置
在这里插入图片描述
在这里插入图片描述

2.8 使用nginx进行管理

修改nginx配置文件
在这里插入图片描述
指定好网络和端口,权重设置都为1
在这里插入图片描述

2.9 使用SwitchHost进行网页统一

在这里插入图片描述

2.10浏览器访问

启动服务
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
点击提交后依然会重新进入login界面

3.问题解决

解决的思路主要有两种:

  1. 将session单独存放到一个服务器,运行的两台服务器在进行服务时都从第三台服务器上读取session
  2. 在用户进行登录后给用户返回特定字符串(JWT技术),每次用户进行后续访问都自动携带这串信息,在服务器上只需进行这串信息合法性判断即可(这里不做过多讨论)

针对第一种方案,使用redis进行session存储,用到的框架就是SpringSession

3.1 依赖导入

        <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>

3.2 application.properties配置

#设置程序启动时默认的配置文件
spring.profiles.active=M1



#进行redis配置
spring.redis.host=172.16.2.134
spring.redis.database=1
spring.redis.port=6379
spring.redis.password=123456

# springSession配置
spring.session.store-type=redis
spring.session.timeout=30m
# 可以配置redis中存储的文件夹
spring.session.redis.namespace=demo1:555

3.3 启动网址

在这里插入图片描述
在这里插入图片描述

3.4 redis查看

这里需要注意的是,必须在controller中拿到session才会触发保存到redis的操作(即request.getSession()),否则不会进行存储
在这里插入图片描述
因为springSession进行自动存储,所以序列化格式采用了JDK默认方式,不太好修改,但是如果我们自己想要在redis中进行存储,可以设置存储格式:

   redisTemplate.setKeySerializer(new StringRedisSerializer());
   redisTemplate.setValueSerializer(new StringRedisSerializer());
   redisTemplate.setHashValueSerializer(new StringRedisSerializer());
   redisTemplate.setHashKeySerializer(new StringRedisSerializer());

版权声明:本文为Guesshat原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。