拦截器之权限注解
接触了项目,在阅读代码时对其架构中拦截器的设计有很多值得记录的地方,在此记录一下。
对拦截器的理解:
在项目架构的搭建中,通过注册拦截器,可以减少很多重复代码,将请求拦截下来进行对应的操作,是一种AOP思想的运用。通过使用拦截器来对请求开始,执行过程,结束时都执行所指定的代码,实现业务与功能代码的解耦,让整个代码的结构更加优雅。
这里说说拦截器的应用之一:权限注解
在项目开发中想要设定权限,根据访问用户来判断其是否有权限进行操作。
- 注解代码 @Permission
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Permission {
String R_销售概览 = "销售概览";
String R_门店管理 = "门店管理";
String R_门店列表 = "门店列表";
String R_ID卡列表 = "ID卡列表";
String R_货柜管理 = "货柜管理";
String R_货柜列表 = "货柜列表";
String R_货架列表 = "货架列表";
String R_日志管理 = "日志管理";
String R_进货单列表 = "进货单列表";
//。。。。此处可以防止各种权限,通过拦截用户操作,判断该用户是否有对应的权限,一般权限是记录再数据库中,通过查询用户权限字段来判断用户权限
String A_查看 = "查看";
String A_删除 = "删除";
/**
* 要检查的权限,包括Action,首字母小写的驼峰形式
*/
String[] resuorce() default "";
String[] actions() default A_查看;
}
- 建立拦截器,此处需要实现
HandlerInterceptor
接口,这里实现了他的前置拦截方法
public class AdminInterceptor4User implements HandlerInterceptor {
private static Logger logger = Logger.getLogger(AdminInterceptor4User.class);
private SystemConfig systemConfig;
private LsAdminUserViewService lsAdminUserViewService;
//此处还用了单例模式
public LsAdminUserViewService getAdminUserService(HttpServletRequest request) {
if (lsAdminUserViewService == null) {
synchronized (this) {
logger.info("动态加载lsCompanyService Bean");
BeanFactory factory = WebApplicationContextUtils.getRequiredWebApplicationContext(request.getServletContext());
lsAdminUserViewService = (LsAdminUserViewService) factory.getBean("lsAdminUserViewService");
}
}
return lsAdminUserViewService;
}
public SystemConfig getSystemConfig(HttpServletRequest request) {
if (systemConfig == null) {
synchronized (this) {
logger.info("动态加载SystemConfig Bean");
BeanFactory factory = WebApplicationContextUtils.getRequiredWebApplicationContext(request.getServletContext());
systemConfig = (SystemConfig) factory.getBean("systemConfig");
}
}
return systemConfig;
}
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
if ("OPTIONS".equals(request.getMethod())) {
return true;
}
if ("GET".equals(request.getMethod())) {
return true;
}
try {
HandlerMethod methodHandle = (HandlerMethod) handler;
preHandleDo(request, response, methodHandle);
} catch (Exception ex) {
logger.info("", ex);
BaseResponse baseResponse = new CmsExceptionHandler().handle(ex);
request.setAttribute("errmsg", baseResponse.getErrmsg());
request.setAttribute("errcode", baseResponse.getErrcode());
response.setStatus(HttpStatus.OK.value());
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json; charset=utf-8");
String h = response.getHeader("Access-Control-Allow-Origin");
if (ValueHelper.isNone(h)) {
String origin = request.getHeader("Origin");
response.setHeader("Access-Control-Allow-Origin", origin);
response.setHeader("Access-Control-Allow-Credentials", "true");
}
try {
response.getWriter().write(JSONObject.toJSONString(baseResponse));
response.getWriter().flush();
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
return true;
}
void preHandleDo(HttpServletRequest request, HttpServletResponse response, HandlerMethod methodHandle) throws Exception {
//获取请求的域名
String referer = request.getHeader("Referer");
if (referer == null) {
if ("dev".equals(getSystemConfig(request).getProfile())) {
referer = request.getParameter("Referer");
}
if (referer == null) {
return;
}
}
URL url = new URL(referer);
String domain = "," + url.getHost() + ",";
//获取用户信息:
Long id = 0L;
String tokenStr = request.getParameter(Constants.ADMIN_PARAM_TOKEN);
if (tokenStr != null) {
id = ValueHelper.tryParseLong(AESUtil.AESDecodeForWeb(tokenStr), 0L);
}
LsAdminUserView adminUser = null;
if (id > 0) {
adminUser = getAdminUserService(request).selectById(id);
if (adminUser != null) {
request.setAttribute(Constants.REQUEST_ADMIN_USER_KEY, adminUser);
request.setAttribute(Constants.REQUEST_ADMIN_USER_ID_KEY, adminUser.getId());
}
}
//此处来校验!!
Permission permission = methodHandle.getMethodAnnotation(Permission.class);
if (permission != null) {
if (adminUser == null) {
throw new OauthFailedException("用户信息验证失败!");
}
String jsonString = adminUser.getPermissionJson();
JSONArray jsonArray = JSONObject.parseArray(jsonString);
List<ResourcesPermission> permissionList = new ArrayList<>();
getRealPermission(jsonArray, permissionList);
Set<String> resource = Arrays.stream(permission.resuorce()).collect(Collectors.toSet());
Set<String> set = Arrays.stream(permission.actions()).collect(Collectors.toSet());
if (permissionList.stream().noneMatch(resourcesPermission -> {
Set<String> temp = new HashSet<>(resourcesPermission.getActions());
temp.retainAll(set);
return resource.contains(resourcesPermission.getName()) && temp.size() != 0;
})) {
throw new NoPermissionException("您没有该权限");
}
}
}
@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler,
Exception ex)
throws Exception {
}
public void getRealPermission(JSONArray jsonArray, List<ResourcesPermission> permissionList) {
for (Object object : jsonArray) {
JSONObject jsonObject = (JSONObject) object;
JSONArray childArray = jsonObject.getJSONArray(ResourcesPermission.CHILDREN_KEY);
if (childArray != null) {
getRealPermission(childArray, permissionList);
} else {
permissionList.add(new ResourcesPermission(jsonObject));
}
}
}
}
- 注册拦截器到项目配置中,此处要继承
WebMvcConfigurerAdapter
这个虚类,这里参考的项目先创建了BaseWebMvcConfigurerAdapter
来继承,再创建AppsServerApplication
继承,由它来实现拦截器配置以及整个SpringBoot项目的启动。
public class BaseWebMvcConfigurerAdapter extends WebMvcConfigurerAdapter {
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
FastJsonHttpMessageConverter fastJsonConverter = new FastJsonHttpMessageConverter();
FastJsonConfig fjc = new FastJsonConfig();
//1、序列化重点
fjc.setSerializerFeatures(SerializerFeature.BrowserCompatible, SerializerFeature.DisableCircularReferenceDetect);
fastJsonConverter.setFastJsonConfig(fjc);
converters.add(fastJsonConverter);
}
}
@SpringBootApplication
@ImportResource("classpath:META-INF/spring/applicationContext.xml") //不要用 *号 保证清晰的引用
public class AppsServerApplication extends BaseWebMvcConfigurerAdapter {
private static Logger logger = Logger.getLogger(AppsServerApplication.class);
public static boolean isStartUp = false;
static public void main(String [] args) {
isStartUp = false;
logger.info(">>>>> huduo Apps Server 正在启动 <<<<<");
SpringApplication.run(AppsServerApplication.class,args);
logger.info(">>>>> huduo Apps Server 启动完成 <<<<<");
isStartUp = true;
}
/**
* 配置拦截器
* @author yanhy
* @param registry
*/
public void addInterceptors(InterceptorRegistry registry) {
//Admin 路径,官网的数据请求
registry.addInterceptor(new AdminInterceptor()).addPathPatterns("/admin/**");
registry.addInterceptor(new AdminInterceptor4User()).addPathPatterns("/admin/**");
registry.addInterceptor(new AndroidInterceptor()).addPathPatterns("/android/**");
registry.addInterceptor(new AndroidInterceptor4User()).addPathPatterns("/android/**");
//租户拦截器
registry.addInterceptor(new TenantInterceptor()).addPathPatterns("/tenant/**");
registry.addInterceptor(new CommonInterceptor()).addPathPatterns("/common/**");
registry.addInterceptor(new CommonInterceptor4User()).addPathPatterns("/common/**");
//根据不同路径设置不同的拦截器
registry.addInterceptor(new MiniProgramInterceptor()).addPathPatterns("/miniProgram/**");
registry.addInterceptor(new MiniProgramInterceptor4User()).addPathPatterns("/miniProgram/**");
}
}
总结
至此,基本的权限注解功能就实现了,以后可以通过这个模板来实现。
将拦截器注册后,以后都直接使用注解就可以实现,极大的简化了开发。
涉及的类或接口:WebMvcConfigurerAdapter
、HandlerInterceptor