创建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("请登录后再操作");
}
}
登录成功后
操作业务
退出
退出后再操作