SpringBoot使用AOP+Redis实现简单的token登录验证示例

创建SpringBoot项目,引入依赖

<!--AOP,这个是必选依赖-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!--Redis,这个是必选依赖-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

<!--hutool工具类,这是个可选依赖-->
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.3.10</version>
</dependency>

思路是这样的:

1、用户登录成功就随机创建一个Token,然后分别存入session中和Redis中
2、AOP中配置切点,和使用@Around注解,判断请求的session中的token是否和redis中的一样,一样就放行,不一样就返回错误提示

我这边使用了前后端分离,所以封装了统一的返回结果类,可以根据需求选择

返回结果类:

package com.common;

import java.io.Serializable;

/**
 * @Classname Result
 * @Description TODO
 * @Date 2020/8/17 4:06
 * @Created by SunZhiQiang
 */
public class Result implements Serializable {

    private int code; //200正常 ,400异常
    private String msg;
    private Object data;

    public static Result success(Object data){

        return new Result(200, "操作成功", data);
    };

    public static Result failed(String msg){

        return new Result(400, msg, null);
    };



    public Result() {
    }

    public Result(int code, String msg, Object data) {
        this.code = code;
        this.msg = msg;
        this.data = data;
    }

    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }

    @Override
    public String toString() {
        return "Result{" +
                "code=" + code +
                ", msg='" + msg + '\'' +
                ", data=" + data +
                '}';
    }
}

先写两个控制器,一个登录和退出用的,一个是模拟其它带请求

登录控制器

package com.controller;

import cn.hutool.core.util.IdUtil;
import com.common.Result;
import com.entity.Users;
import com.service.UsersService;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpSession;

/**
 * @Classname LoginController
 * @Description TODO
 * @Date 2020/8/23 3:01
 * @Created by SunZhiQiang
 */
@RestController
public class LoginController {
    // 业务
    @Autowired
    private UsersService usersService;
    // 注入HttpSession
    @Autowired
    private HttpSession httpSession;

    // 注入SpringBoot中的Redis操作模板,,需要要pom文件中引入
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @ApiOperation("用户登录")
    @GetMapping("login")
    public Result login(String userName) {
        // 从数据库中查询用户
        Users users = usersService.queryById("1");

        // 判断是否正确
        if (userName.equals(users.getUserName())){
            // 这边使用了Hutool工具类随机生成不带-的UUID来作为token,也可以JDK自带的UUID生成然后替换掉-
            String token = IdUtil.simpleUUID();

//        Cookie cookie = new Cookie("token", token);
//        -1表关闭浏览器后cookie就失效
//        cookie.setMaxAge(-1);
//        response.addCookie(cookie);

            // 在session中添加token
            httpSession.setAttribute("token", token);

            // 在Redis中添加token
            stringRedisTemplate.opsForValue().set("token", token);

            // 正确返回成功的提示
            return Result.success("登录成功");

        }

        // 不正确返回错误的提示
        return Result.failed("用户或者密码不正确");
    }

    @ApiOperation("用户退出")
    @GetMapping("loginOut")
    public Result loginOut() {

//        删除cookie就是新建一个同名的空值cookie
//        response.addCookie(new Cookie("token", null));

        // 移除session中的token
        httpSession.removeAttribute("token");

        // 移除Redis中的token
        stringRedisTemplate.delete("token");

        // 返回成功提示
        return Result.success("退出成功");
    }
}

模拟其它业务接口

package com.controller;

import com.common.Result;
import com.entity.Users;
import com.service.UsersService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;

/**
 * 用户表(Users)表控制层
 *
 * @author makejava
 * @since 2020-08-20 14:10:01
 */
@RestController
@Api("用户管理")
@CrossOrigin
public class UsersController {
    /**
     * 服务对象
     */
    @Resource
    private UsersService usersService;

    /**
     * 通过主键查询单条数据
     *
     * @param id 主键
     * @return 单条数据
     */
    @ApiOperation("根据id查询用户")
    @GetMapping("selectOne")
    public Result selectOne(String id) {
        Users users = usersService.queryById(id);
        if (users!=null){
            Result success = Result.success(users);
            return Result.success(users);
        }

        return Result.failed("密码或帐户名错误");
    }


}

AOP类

package com.aop;

import com.common.Result;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpSession;

/**
 * @Classname LogAspect
 * @Description TODO
 * @Date 2020/8/21 15:38
 * @Created by SunZhiQiang
 */
@Aspect
@Component
public class LogAspect {

    // 注入HttpSession
    @Autowired
    private HttpSession httpSession;

    // SpringBoot中的Redis操作模板,需要要pom文件中引入
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    // 增加切点
    // 第一个*任意返回值
    // com.controller.UsersController表示com.controller包下的UsersController类,
    // "execution(* com.controller.*.*(..))"也可以*代替UsersController就表示包下的任意类
    // 第二个*表示任意方法
    // 第三个(..)表示任意参数
    // 这段就表示在拦截com.controller包下的UsersController类中任意返回值,任意参数的所有方法,你也可以具体到某个方法
    @Pointcut("execution(* com.controller.UsersController.*(..))")
    // 任意空方法,主要是用来放上面的注解
    public void LogAspect() {}

    
    // 注解里面填的是切点方法名
    // ProceedingJoinPoint proceedingJoinPoint 这个是@Around注解的固定参数
    @Around("LogAspect()")
    public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {

        // 从数据库获取登录控制器中创建并存入redis中的token
        String s = stringRedisTemplate.opsForValue().get("token");

        // 如果redis中的token不为空再获取session中的token判断
        if (s!=null){
            // 获取session中的token,也可以用cookie,但是要循环,麻烦点
            String token = (String)httpSession.getAttribute("token");

//        Cookie[] cookies = request.getCookies();
//        for (Cookie cookie : cookies) {
//            if (s.equals(cookie.getValue())){
//                return proceedingJoinPoint.proceed();
//            }
//        }

            // 比较session中和redis中的token,一致就进入切点方法,否则直接返回失败结果
            if (s.equals(token)){
                // token正确,放行,会自己跳转继续执行目标方法
                return proceedingJoinPoint.proceed();
            }
        }

        // token不正确返回错误
        return Result.failed("请登录后再操作");
    }
}

登录成功后

在这里插入图片描述

操作业务

在这里插入图片描述

退出

在这里插入图片描述

退出后再操作

在这里插入图片描述

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值