基于 SpringBoot + Vue + redis 实现的 token 功能(对登陆后的请求添加验证、拦截功能)

场景:在前后端分离(SpringBoot + Vue)的项目中,部分网站需要在登陆之后才可以访问其他页面进行相关操作(例如:某某某后台管理系统),这时,在用户登录之后就需要后端给前端发送一个令牌,前端接收后保存在本地(Session Storage),往后前端发送的每个请求都需要携带该令牌,后端验证之后才可以响应数据给前端,这样就不会被没有登陆的人员恶意的访问后端,并篡改数据库的相关数据了。

  上述场景说的令牌就是token了,在往常没有前后端分离的项目中,实现诸如此类的功能一般是使用 cookie 和 session 的,但在前后端分离的项目中,前端和后端是跨域的,这时候 cookie 和 session 就不起作用了,所以需要 token 的存在。

  以下是对token的简单实现:

详细步骤:

一、创建管理员表admin

在这里插入图片描述

二、redis配置

1、导入依赖
<!-- 配置使用redis启动器,存储token -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2、在 application.properties 中配置相关信息
# 配置redis
spring.redis.host=xxx.xxx.xx.xx # 安装了redis的服务器公网ip
spring.redis.port=6379 # redis使用的端口号,一般是6379

三、在SpringBoot中创建admin表对应的domain、controller、service、dao层

1、domain层代码
/**
 * 管理员表对应的实体类
 */
public class Admin implements Serializable {
    // 主键
    private Integer id;
    // 管理员昵称
    private String adminName;
    // 管理员登陆密码
    private String password;

    /**
     * 相应的get()、set()方法,这里省略
     */
}
2、dao层及相应sql的xml代码
@Repository
public interface AdminMapper {
    /**
     * 对登陆的管理员进行验证
     */
    Admin verifyAdmin(String adminName, String password);
}

xml代码:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC
        "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="xxxx.xxxx.xxxx.mapper.AdminMapper">
    <!--  验证用户输入的账号密码是否正确  -->
    <select id="verifyAdmin" resultType="Admin">
        select * from admin where admin_name=#{adminName} and password=#{password}
    </select>
</mapper>
3、service层接口及其实现类
/**
 * 管理员的业务层接口
 */
public interface AdminService {
    /**
     * 验证管理员账号
     * @param adminName
     * @param password
     * @return
     */
    Admin verifyAdmin(String adminName, String password);
}
/**
 * 管理员的业务层接口的实现类
 */
@Service
public class AdminServiceImpl implements AdminService {

    @Autowired
    private AdminMapper adminMapper;

    /**
     * 验证管理员账号
     *
     * @param adminName
     * @param password
     * @return
     */
    @Override
    public Admin verifyAdmin(String adminName, String password) {
        return adminMapper.verifyAdmin(adminName, password);
    }

}
4、controller层代码
/**
 * 管理员控制类
 */
@RestController
@RequestMapping("/admin")
public class AdminController {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private AdminService adminService;

    /**
     * 验证管理员的账号密码是否正确
     * @param adminName
     * @param password
     * @return
     */
    @PostMapping("/verify")
    public Object verifyAdmin(String adminName, String password) {
        JSONObject jsonObject = new JSONObject();
        Admin admin = adminService.verifyAdmin(adminName, password);
        if (admin != null) {
            // 使用UUID作为token值
            String uuid = UUID.randomUUID().toString().replaceAll("-", "");
            // 在uuid后拼接管理员id组成最后的token值(添加id是为了方便后续验证)
            String token = uuid + admin.getId();
            // 将用户的ID信息存入redis缓存,并设置两小时的过期时间
            stringRedisTemplate.opsForValue().set("admin"+admin.getId(), token, 7200, TimeUnit.SECONDS);
            jsonObject.put(Consts.CODE, 1);
            jsonObject.put(Consts.MSG, "登陆成功");
            jsonObject.put("token", token);
        }else {
            jsonObject.put(Consts.CODE, 0);
            jsonObject.put(Consts.MSG, "账号或者密码错误");
        }
        // 向前端返回相应的json数据
        return jsonObject;
    }

}

  这里多解释一下,这里生成的 token 值其实就是使用了 UUID 加上查询出来的管理员id,然后将其存储在 redis 中,并且其key值固定格式为 "admin"+admin.getId() (如:admin1)。

  这样可以让一个管理员对应 redis 里一条 key-value 键值对数据,这样在管理员多次登陆后数据在 redis 中都会被替代掉(因为添加时 key 值相等),避免在 redis 存储了之前 token 无效数据,并且在后续在拦截器中验证,获取到 token 值时,就可以得到管理员的 id,以此为依据在 redis 中查询其 token 值是否是伪造的。这时就可以启动后端接收前端的请求了。

四、前端

1、前端发送登录请求

前端发送登录请求成功后将后端返回的 token 值保存在浏览器的 Session Storage 中,代码如下:

// 拼接请求参数(管理员名称和密码)
let params = new URLSearchParams();
params.append("adminName", this.loginForm.adminName);
params.append("password", this.loginForm.password);
// 向后端发送验证请求
post(`admin/verify`, params).then(res => {
    if(res.code == 1) {
        // 登陆成功之后将后端返回的值保存在浏览器的 Session Storage 中
        window.sessionStorage.setItem("token", res.token);
        this.$router.push("/home");
        this.$message.success("登陆成功")
     }else {
        this.$message.error("账号或者密码错误")
     }
})
2、封装axios
import axios from 'axios'
import router from '../router';

axios.defaults.timeout = 15000; // 响应的超时时间
axios.defaults.withCredentials = true; // 支持跨域访问
// 为post请求设置请求头
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=UTF-8';
axios.defaults.baseURL = "http://xxxxxxxxxxx:xxxx/"
// 发送请求之前先为请求头添加Authorization字段且值为token,以应对后端接口的验证
axios.interceptors.request.use(config => {
    // 在浏览器的 Session Storage 中拿到 token 值
    config.headers.Authorization = window.sessionStorage.getItem("token");
    return config;
})

这样前端发送的每一个请求在其头部信息中都会携带我们自定义的 Authorization 属性,其值就是登陆时后端返回的 token 值。

五、拦截器

1、后端添加拦截器

后端添加拦截器以验证请求中携带的 token 值是否真实存在,代码如下:

/**
 * 访问controller之前先进行token验证
 */
@Component
public class TokenInterceptor implements HandlerInterceptor {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 注意:放行浏览器的预检请求
        if (request.getMethod().equals("OPTIONS")) {
            return true;
        }
        // 获取请求头中携带的token值
        String token = request.getHeader("Authorization");
        // token验证
        if(token!=null) {
            // token值的最后一位数字是管理员的id,也是redis中存储token的key
            String adminId = "admin" + token.substring(token.length() - 1);;
            String value = stringRedisTemplate.opsForValue().get(adminId);
            if (value.equals(token)) {
                return true;
            }
        }
        // 验证失败,拦截请求
        return false;
    }
}

  拦截器根据token携带的管理员id值(key)在redis中查找相应的结果(value),再与请求携带的token值比较,一样则放行请求。

注意:上述代码 “放行浏览器的预检请求” 部分需要特别注意,缺少这段if判断代码,可能会引起前端的CORS错误,导致请求一直被拦截,无法通过,详情见本人的另一文章,点击前往

2、配置自定义拦截器
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Autowired
    private TokenInterceptor tokenInterceptor;
    
    /**
     * 配置token拦截器生效
     * @param registry
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 配置自定义拦截器,使其生效,并且放行登陆请求
        registry.addInterceptor(tokenInterceptor).addPathPatterns("/**").excludePathPatterns("/admin/verify");
    }    

    /**
     * 解决因前后端的端口不一致导致的跨域问题
     */
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOrigins("*")
                .allowedMethods("GET","POST","PUT","OPTIONS","DELETE","PATCH")
                .allowCredentials(true)
                .maxAge(3600);
    }
      
}

到此基于 SpringBoot + Vue + redis 简单实现的 token 功能就小功告成了。

本文仅个人纪录学习所用,如有纰漏,欢迎指正。

  • 8
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
权限管理系统是一种用于管理用户权限和角色的系统,可以根据用户的身份和角色来控制其访问系统中的各种资源。基于SpringBootVueRedis的前后端分离模式,可以更好地实现权限管理系统的功能。 在这个系统中,SpringBoot作为后端框架,提供了强大的功能和稳定的性能,可以处理用户的请求并进行权限验证Vue作为前端框架,提供了友好的界面和良好的用户体验,可以让用户方便地进行权限管理操作。而Redis作为缓存数据库,可以用来存储权限信息和用户的登录状态,加快系统的响应速度和提高系统的性能。 在权限管理系统中,我们可以使用RBAC(基于角色的权限控制)模型,将用户分配到不同的角色,再将角色分配到不同的权限,从而实现对用户访问资源的控制。通过这种方式,可以实现灵活的权限管理,并且可以根据实际需求动态地调整用户的权限和角色。 通过使用SpringBootVue,我们可以实现前后端分离,让前端和后端分别进行开发和维护,降低了系统的耦合度,同时也增加了系统的灵活性和可维护性。而通过使用Redis,我们可以充分利用其高速的读取和写入能力,有效地提升系统的性能和响应速度。 综上所述,基于SpringBootVueRedis的权限管理系统,可以实现灵活、高效和安全的权限管理功能,满足用户对于权限管理的各种需求。同时,前后端分离模式也使得系统更加灵活和易于维护。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值