我们经常会遇到这样的事情,我们登录某个网页只可以浏览,点击某些操作时他就会提示我们用户未登录,或者是登录好了长时间不操作,提示我们登录超时请重新登录。其实这两种都可以用过滤器+Redis来简单实现,下面我们简单实现一种,“长时间不操作,提示我们登录超时请重新登录”。
了解几个概念
token令牌:是服务端生成的一串随机生成字符串(我们的例子用UUID生成即可),放在请求头作为客户端进行请求的一个标识。 当用户第一次登录,服务器生成一个token并将此token返回给客户端,以后客户端只需带上这个token前来请求数据即可判断是谁在调用接口,无需再要用户名密码等。
localStorage:用于长久全局保存整个客户端网站的数据,没有过期时间,知道手动删除。也就是把值存到里面后,在任何一个客户端网页都可以取到他们的值。可以用来存登录成功后:显示欢迎xxx!
存值:localstorage.setItem("name","value") 简写:localstorage.name = "value"。
取值:localstorage.getItem("name") 简写:value = localstorage.name。
步骤1:springboot整合Redis,MySQL,Mybatis
1.pom依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.5</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.zx.token</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>token_test</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!--springBoot依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--mysql和JDBC依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.30</version> <!--重新设置版本,太低匹配不了云数据库-->
<scope>runtime</scope>
</dependency>
<!--阿里巴巴数据库连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.5</version>
</dependency>
<!--Mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.2</version>
</dependency>
<!--实体方法-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
<scope>provided</scope>
</dependency>
<!--阿里巴巴json格式-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.35</version>
</dependency>
<!--javaweb的依赖 RequestMappering....等-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--Redis的操作工具jedis-->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.2.0</version>
</dependency>
<!--Redis的连接工具-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--连接池-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<!--test测试工具,不是springBoottest的工具-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
2.yml文件配置
spring:
#MySQL配置
datasource:
username: root
password: 123
url: jdbc:mysql://localhost:3306/zx?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
type: com.alibaba.druid.pool.DruidDataSource
#Redis配置
redis:
host: 127.0.0.1
port: 6379
database: 0 #连接的是那个库一共16个0~15,默认0
#mybatis配置及取别名
mybatis:
mapper-locations: classpath:mapper/*.xml
type-aliases-package: com.zx.token.entity
步骤2:写过滤器
1.FilterConfig:注册过滤器,并声明过滤条件(声明)。
2.MyFilter:经过过滤器需要执行的具体业务(如何处理)。(主要是doFilter方法)
package com.zx.token.config;
import com.zx.token.filter.MyFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class FilterConfig {
@Autowired
MyFilter myFilter;
@Bean
public FilterRegistrationBean filterRegistrationBean(){
//注册过滤器(初始化过滤器)
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(myFilter);
//添加过滤的路径,凡是路径带/user就进入过滤器
filterRegistrationBean.addUrlPatterns("/user/*");
return filterRegistrationBean;
}
}
package com.zx.token.filter;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
@Component
public class MyFilter implements Filter {
@Resource //作用和@Autowired,只是他按名字注入,而@Autowired按类型注入
private RedisTemplate<String,Object> redisTemplate;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("-----------------过滤器初始化----------------");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
//校验用户登录状态
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
System.out.println("执行过滤器");
//取出token
String token = request.getHeader("token");
//第一次登录没有token,给null会报错,所以我们判断一下token是否为空,为空给一个空串
//三元运算
token = token==null?"":token;
//查询Redis中token的剩余时间
Long expire =redisTemplate.getExpire(token);
if(expire > 0){
//时间大于0 放行
/*
有一个问题,用户一直在网页上操作,但是token一直在倒计时,操作着操作着就变未登录了
不合理,所以,当用户在操作时,每次经过过滤器调用不同接口时,就重置一下token的时间
*/
redisTemplate.expire("token",1L, TimeUnit.MINUTES);
//放行
filterChain.doFilter(servletRequest,servletResponse);
}else {
System.out.println("未登录!");
return;
}
}
@Override
public void destroy() {
System.out.println("过滤器已经死亡!");
}
}
步骤3:controller层,主要在该层操作,其他层的正常书写。
package com.zx.token.controller;
import com.alibaba.fastjson.JSON;
import com.zx.token.entity.User;
import com.zx.token.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.time.Duration;
import java.util.UUID;
@RestController
public class UserController {
@Autowired
UserService service;
@Resource //作用和@Autowired,只是他按名字注入,而@Autowired按类型注入
private RedisTemplate<String,Object> redisTemplate;
@RequestMapping("/login")
public String login(User luser,HttpServletRequest request){
User user = service.login(luser.getName(),luser.getPsd());
if(user!=null){
//生成一个token令牌 用UUID生成一个唯一标识
String token = UUID.randomUUID()+"";
//存到Redis中
redisTemplate.opsForValue().set(token,user, Duration.ofMinutes(1L));
return token;
}
return null;
}
@RequestMapping("/user/use")
public String getUserOfLogin(HttpServletRequest request){
//获取请求头中的token参数
String token = request.getHeader("token");
User user = (User) redisTemplate.opsForValue().get(token);
if (user!=null){
String jsonString = JSON.toJSONString(user);
return jsonString;
}
return null;
}
}
步骤4:用HTML测试效果。
1.index.html:登录页面
2.show.html:操作页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<script src="https://cdn.staticfile.org/jquery/2.1.1/jquery.min.js"></script>
<title>登录</title>
</head>
<body>
<form id="login">
用户名: <input type="text" id="username"/><br>
密 码:<input type="password" id="psd"/><br>
<input type="button" value="登录" onclick="login()"/><br>
</form>
</body>
</html>
<script>
function login() {
const user = {name: $("#username").val(),psd: $("#psd").val()}
$.ajax({
url: "http://localhost:8080/login",
data:user,
success:function (longinres) {
if(longinres != null && longinres != ''){
alert("登录成功!")
//存储token到客户端全局
localStorage.token = longinres;
window.location.href="show.html"
}else {
alert("登录失败,请检查!")
}
}
})
};
</script>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<script src="https://cdn.staticfile.org/jquery/2.1.1/jquery.min.js"></script>
<title>主页面</title>
</head>
<body>
<div id="div"></div>
<h3>你的具体信息如下: <button onclick="test()">获取</button></h3>
<hr>
<p id="p"></p>
<hr>
</body>
</html>
<script>
$.ajax({
url: "http://localhost:8080/user/use",
headers:{"token":localStorage.token},
success:function (res) {
if(res != null && res != ''){
const data = eval("("+res+")")
$("#div").append("<h1>欢迎您:"+data.name +"!</h1>")
}else{
alert("你还未登录!")
window.location.href="index.html"
}
}
});
function test() {
$.ajax({
url: "http://localhost:8080/user/use",
headers:{"token":localStorage.token},
success:function (res) {
if(res != null && res != ''){
const data = eval("("+res+")")
var str =""
str+="编号:"+data.id+"<br>";
str+="账户:"+data.name+"<br>";
str+="密码:"+data.psd+"<br>";
$("#p").append(str)
}else{
alert("登录超时!")
window.location.href="index.html"
}
}
});
};
</script>
测试效果