分布式session
思路:
(1)用户登录
- 失败:返回错误信息
- 成功:生成随机字符串token(
uuid
),用户信息存入redis中
(2)获取用户
从cookie
中获取token
,再从redis中获取用户对应信息
- 实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class User {
private Integer id;
private String username;
private String password;
// 1 为管理员用户
// 2 为普通用户
private Integer userType;
}
- 用hashmap模拟redis
public class CacheUtil {
public static Map<String, User> userCache = new HashMap<>();
}
- 接收用户登录VO类
@Data
public class LoginVO {
private String username;
private String password;
}
- controller
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/login")
public String login(LoginVO loginVO, HttpServletResponse response) {
return userService.login(loginVO, response);
}
// 1 管理员
// 2. 普通用户
// 判断用户类型 开始的版本 每次需要获取用户信息都得这样判断,太重复了
@GetMapping("/index")
public String index(@CookieValue("token")Cookie cookie) {
// 从cookie中获取token对应的值,再到‘redis‘中获取用户
String value = cookie.getValue();
User user = CacheUtil.userCache.get(value);
if (user.getUserType() != 1) {
return "你是普通用户,权限不足";
}
return user.toString();
}
@GetMapping("/index2")
public String index(User user) {
if (user.getUserType() != 1) {
return "你是普通用户,权限不足";
}
return user.toString();
}
@GetMapping("/index3")
@AccessLimit //自定义的拦截器注解
public String index3(User user ) {
return "limit 验证管理员通过" + user.toString();
}
}
- UserService
@Service
public class UserService{
public static Set<User> dbUser = new HashSet<>();
// 模拟数据库用户
static {
dbUser.add(new User().setId(1).setUsername("admin").setPassword("admin").setUserType(1));
dbUser.add(new User().setId(2).setUsername("user").setPassword("user").setUserType(2));
dbUser.add(new User().setId(3).setUsername("zhangsan").setPassword("123456").setUserType(2));
}
public String login(LoginVO loginVO, HttpServletResponse response) {
Optional<User> userOptional = dbUser.stream().filter(u -> u.getUsername().equals(loginVO.getUsername())
&& u.getPassword().equals(loginVO.getPassword())).findFirst();
if (userOptional.isPresent()) {
User user = userOptional.get();
//验证通过生成token
String token = UUID.randomUUID().toString();
Cookie cookie = new Cookie("token",token);
cookie.setPath("/");
//用户信息存入缓存中 模拟
CacheUtil.userCache.put(token,user);
response.addCookie(cookie);
return "登录成功:" + user.toString();
} else {
return "用户名或者密码错误";
}
}
//从request中获取cookie中对应的用户信息
public static String getCookieValue(HttpServletRequest request) {
Cookie[] cookies = request.getCookies();
if (cookies == null || cookies.length <= 0) {
return null;
}
for (Cookie cookie : cookies) {
if (cookie.getName().equals("token")) {
return cookie.getValue();
}
}
return null;
}
}
像上面的/index1
,只要一需要用户信息,都需要从cookie中,再从redis中获取信息,太繁琐了,所以让我们自定义一个参数解析器,直接获取用户信息
- 自定义
UserArgumentResolver
@Configuration
public class UserArgumentResolver implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter methodParameter) {
Class<?> clazz = methodParameter.getParameterType();
// 只要controller中使用User作为参数(像/index2,/index3那样),就执行
return clazz == User.class;
}
// 把之前的验证拿到这来
@Override
public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {
HttpServletRequest request = nativeWebRequest.getNativeRequest(HttpServletRequest.class);
HttpServletResponse response = nativeWebRequest.getNativeRequest(HttpServletResponse.class);
// 获取 cookie
String cookieValue = UserService.getCookieValue(request);
if (cookieValue != null) {
User user = CacheUtil.userCache.get(cookieValue);
return user;
}
return null;
}
}
UserArgumentResolver
添加到WebMvcConfigurer
方法里面
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private UserArgumentResolver userArgumentResolver;
// @Autowired
// private AccessInterceptor accessInterceptor;
//参数
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(userArgumentResolver);
}
//自定义拦截器注解 后面用到的
// @Override
// public void addInterceptors(InterceptorRegistry registry) {
// registry.addInterceptor(accessInterceptor);
// }
}
这样就像上面/index2
添加User参数获取对应信息了
如:
@GetMapping("/index2")
public String index(User user) {
if (user.getUserType() != 1) {
return "你是普通用户,权限不足";
}
return user.toString();
}
自定义拦截器注解
但我有些接口只能让管理员(userType=1
)才能访问呢
不可能每次都自己手写重复代码
if (user.getUserType() != 1) {
return "你是普通用户,权限不足";
}
这样也太麻烦了吧,所以我们用到了自定义拦截器注解
- 定义注解
AccessLimit
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface AccessLimit {
int type() default 2;//默认是普通用户 其实没有用到
}
- 定义拦截器
AccessInterceptor
@Configuration
public class AccessInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
response.setContentType("text/html;charset=UTF-8");
if(handler instanceof HandlerMethod){
HandlerMethod hm = (HandlerMethod) handler;
AccessLimit accessLimit = hm.getMethodAnnotation(AccessLimit.class);
// 如果没有使用 accessLimit 就直接放行
if(accessLimit==null){
return true;
}
String cookieValue = UserService.getCookieValue(request);
if(cookieValue==null){
response.getWriter().write("please login");
return false;
}
User user = CacheUtil.userCache.get(cookieValue);
if(user.getUserType()!=1){
response.getWriter().write("只有管理员才能访问!!!");
return false;
}
}
return true;
}
}
- 添加到
WebConfig
其实打开
WebConfig
里面注释的代码即可
@Autowired
private AccessInterceptor accessInterceptor;
//自定义拦截器注解
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(accessInterceptor);
}
这下就添加一个注解@AccessLimit
即可判断userType是否满足条件了
如:
@GetMapping("/index3")
@AccessLimit
public String index3(User user ) {
return "limit 验证管理员通过" + user.toString();
}