Spring Boot (v2.0.5.RELEASE)
程序中有些资源(接口)是需要用户登录才能够使用的,或者是具有某种角色的用户(比如普通登录用户,或者系统管理员等)才能使用,本篇文章先为大家讲解如何控制使用某接口要求用户必须登录。
实现的思路是
首先定义注解
@LoginUser,该注解用于标注哪些接口需要进行拦截定义拦截器,拦截标注了
@LoginUser注解的接口拦截之后判断该用户目前是不是处于登陆状态,如果是登陆状态则放行该请求,如果未登录则提示登陆
给方法或者类打上
@LoginUser注解进行测试
定义标注注解
@LoginUser
package com.futao.springmvcdemo.annotation;import com.futao.springmvcdemo.model.enums.Role;import java.lang.annotation.*;/**
* @author futao
* Created on 2018/9/19-14:39.
* 登陆用户,用户角色
*/@Target(value = {
ElementType.METHOD,
ElementType.TYPE
})@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface LoginUser { /**
* 要求的用户角色
*
* @return
*/
Role role() default Role.Normal;
}2。 定义拦截器LoginUserInterceptor
package com.futao.springmvcdemo.annotation.impl;import com.alibaba.fastjson.JSON;import com.futao.springmvcdemo.annotation.LoginUser;import com.futao.springmvcdemo.model.entity.constvar.ErrorMessage;import com.futao.springmvcdemo.model.system.RestResult;import com.futao.springmvcdemo.model.system.SystemConfig;import com.futao.springmvcdemo.utils.ThreadLocalUtils;import org.apache.commons.lang3.ObjectUtils;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.stereotype.Component;import org.springframework.web.method.HandlerMethod;import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;import javax.annotation.Resource;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import javax.servlet.http.HttpSession;/**
* @author futao
* Created on 2018/9/19-14:44.
* 对请求标记了LoginUser的方法进行拦截
*/@Componentpublic class LoginUserInterceptor extends HandlerInterceptorAdapter { private static final Logger logger = LoggerFactory.getLogger(LoginUserInterceptor.class); @Resource
private ThreadLocalUtils<String> threadLocalUtils; /**
* 在请求到达Controller之前进行拦截并处理
*
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if (handler instanceof HandlerMethod) { //注解在方法上
LoginUser loginUserAnnotation = ((HandlerMethod) handler).getMethodAnnotation(LoginUser.class); //注解在类上
LoginUser classLoginUserAnnotation = ((HandlerMethod) handler).getMethod().getDeclaringClass().getAnnotation(LoginUser.class); if (ObjectUtils.anyNotNull(loginUserAnnotation, classLoginUserAnnotation)) {
HttpSession session = request.getSession(false); //session不为空
if (ObjectUtils.allNotNull(session)) {
String loginUser = (String) session.getAttribute(SystemConfig.LOGIN_USER_SESSION_KEY); if (ObjectUtils.allNotNull(loginUser)) {
System.out.println("当前登陆用户为:" + loginUser); //将当前用户的信息存入threadLocal中
threadLocalUtils.set(loginUser);
} else {
System.out.println("用户不存在"); return false;
}
} else {//session为空,用户未登录
RestResult restResult = new RestResult(false, "-1", ErrorMessage.NOT_LOGIN, ErrorMessage.NOT_LOGIN.substring(6));
response.getWriter().append(JSON.toJSONString(restResult)); return false;
}
}
} return true;
} @Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { //释放threadLocal资源
threadLocalUtils.remove();
}
}注册拦截器
package com.futao.springmvcdemo.annotation;import com.futao.springmvcdemo.annotation.impl.LoginUserInterceptor;import com.futao.springmvcdemo.annotation.impl.RequestLogInterceptor;import com.futao.springmvcdemo.annotation.impl.SignInterceptor;import org.springframework.boot.SpringBootConfiguration;import org.springframework.web.servlet.config.annotation.InterceptorRegistry;import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;import javax.annotation.Resource;/**
* @author futao
* Created on 2018/9/18-15:15.
*/@SpringBootConfigurationpublic class WebMvcConfiguration implements WebMvcConfigurer { @Resource
private SignInterceptor signInterceptor; @Resource
private LoginUserInterceptor loginUserInterceptor; @Resource
private RequestLogInterceptor requestLogInterceptor; /**
* addInterceptor()的顺序需要严格按照程序的执行的顺序
*
* @param registry
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(requestLogInterceptor).addPathPatterns("/**");
registry.addInterceptor(loginUserInterceptor).addPathPatterns("/**"); // "/**"和"/*"是有区别的
registry.addInterceptor(signInterceptor).addPathPatterns("/**");
}
}测试(可分别将注解打在类上和方法上进行测试)
package com.futao.springmvcdemo.controller;import com.alibaba.fastjson.JSON;import com.alibaba.fastjson.JSONArray;import com.alibaba.fastjson.JSONObject;import com.futao.springmvcdemo.annotation.LoginUser;import com.futao.springmvcdemo.model.entity.User;import com.futao.springmvcdemo.model.system.SystemConfig;import com.futao.springmvcdemo.service.UserService;import org.apache.commons.lang3.ObjectUtils;import org.springframework.http.MediaType;import org.springframework.web.bind.annotation.*;import javax.annotation.Resource;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpSession;import java.util.List;import java.util.UUID;/**
* @author futao
* Created on 2018/9/19-15:05.
*/@RequestMapping(path = "User", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)@RestControllerpublic class UserController { @Resource
private UserService userService; /**
* 获取当前的登陆的用户信息,其实是从threadLocal中获取
*
* @return
*/
@LoginUser
@GetMapping(path = "my") public JSONObject my() {
JSONObject jsonObject = new JSONObject();
jsonObject.put("当前的登陆的用户是:", userService.currentUser()); return jsonObject;
} /**
* 模拟登陆接口
*
* @param mobile
* @param request
* @return
*/
@PostMapping(path = "login") public JSONObject login(
@RequestParam("mobile") String mobile,
HttpServletRequest request
) {
HttpSession session = request.getSession();
session.setAttribute(SystemConfig.LOGIN_USER_SESSION_KEY, String.valueOf(UUID.randomUUID()));
session.setMaxInactiveInterval(SystemConfig.SESSION_INVALIDATE_SECOND); return new JSONObject();
}
}测试
4.1 未登录情况下调用标记了@LoginUser的获取当前登陆用户信息接口未登录
4.2 登录登录操作
4.3 登录之后调用调用标记了@LoginUser的获取当前登陆用户信息接口登陆之后
稍微解释一下上面登陆和获取用户信息的逻辑:
用户请求登陆之后,会为该用户在系统中生成一个HttpSession,同时在系统中有一个Map来存放所有的session信息,该Map的key为一个随机字符串,value为session对象在系统中的堆地址,在登陆请求完成之后,系统会将该sesion的key值以cookie(JSESSIONID)的形式写回浏览器。
设置cookie
用户下次登陆的时候,请求中会自动带上该cookie,所以我们在标记了需要登陆的@LoginUser注解的请求到达处理逻辑之前进行拦截,就是从cookie中(JSESSIONID)取出session的key值,如果没有该cookie,则代表用户没有登陆,如果有该cookie,再在存放cookie的map中取,如果没有取到,则代表用户的session已经过期了,需要重新登陆,或者cookie是伪造的。
拿到了登陆用户的session之后,我们去Map中获取对应的值,一般是用户的id,在通过这个用户id,可以去数据库查该用户的信息,查到用户的信息之后将用户信息放入threadLocal中,然后就可以在任何地方get()到当前登陆的用户信息了,非常方便。
使用上面的基于注解的拦截器可以实现很多功能,比如动态的第三方接口验签,和系统日志记录(不需要注解)等
日志系统
作者:FutaoSmile丶
链接:https://www.jianshu.com/p/657fa7118e84
共同学习,写下你的评论
评论加载中...
作者其他优质文章




