自定义注解实现权限管理框架或其它功能扩充

时间:2021-09-15 18:55:41

在进行springMVC进行J2EE项目开发时,spring及第三方的如Shiro等为我们快速实现某个功能提供了注解标签,配置和使用都及其简单

但在某些情况下,需要根据项目需要,自定义某些功能时就会用到今天讲到的自定义标签

 

以进行简单的权限拦截为例来进行讲解

 

当某个controller中的方法,需要具有某个权限或者角色的人员才能执行时,我们分如下几步进行自定义的权限拦截

第一步:定义注解标签

 1 package net.zicp.xiaochangwei.web.annotation;
2
3 import java.lang.annotation.Documented;
4 import java.lang.annotation.ElementType;
5 import java.lang.annotation.Retention;
6 import java.lang.annotation.RetentionPolicy;
7 import java.lang.annotation.Target;
8
9 /**
10 *
11 * @author xiaochangwei
12 * 自定义权限标签
13 */
14 @Target(ElementType.METHOD)
15 @Retention(RetentionPolicy.RUNTIME)
16 @Documented
17 public @interface SelfPermission {
18 String value() default "";
19 }

第二步:在xml中配置interceptor

<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="net.zicp.xiaochangwei.web.interceptors.SelfPermissionInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>

第三步:实现对应的interceptor bean

package net.zicp.xiaochangwei.web.interceptors;

import java.lang.annotation.Annotation;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import net.zicp.xiaochangwei.web.annotation.SelfPermission;
import net.zicp.xiaochangwei.web.common.Result;
import net.zicp.xiaochangwei.web.entity.Permission;
import net.zicp.xiaochangwei.web.entity.Role;

import org.apache.commons.collections4.IterableUtils;
import org.apache.commons.collections4.Predicate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;

/**
* 对有@SelfPermission标签的方法进行拦截,模拟进行权限检查
*
*
@author xiaochangwei
*
*/
public class SelfPermissionInterceptor implements HandlerInterceptor {

@Autowired
private RedisTemplate<String, String> redisTemplate;

@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler)
throws Exception {

request.setCharacterEncoding(
"UTF-8");
response.setCharacterEncoding(
"UTF-8");

if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod
= (HandlerMethod) handler;
SelfPermission permission
= getAnnotation(handlerMethod, SelfPermission.class);
if (permission != null) {
try {
return checkPermission(permission.value());
}
catch (Exception e) {
Result result
= new Result("120001", "没得权限", Result.Status.ERROR);
response.setContentType(
"text/plain;charset=UTF-8");
response.getWriter().write(JSONObject.toJSONString(result));
return false;
}
}
}
return true;
}

@Override
public void afterCompletion(HttpServletRequest arg0,
HttpServletResponse arg1, Object arg2, Exception arg3)
throws Exception {

}

@Override
public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1,
Object arg2, ModelAndView arg3)
throws Exception {
}

public boolean checkPermission(final String permissionCode)
throws Exception {
String cachedRole
= redisTemplate.opsForValue().get("role1");
if (cachedRole != null) {
System.out.println(
"缓存中找到权限数据");
Role jrole
= JSON.parseObject(cachedRole, Role.class);
boolean result = IterableUtils.matchesAny(jrole.getPermissions(),
new Predicate<Permission>() {
public boolean evaluate(Permission p) {
return permissionCode.equals(p.getPermission());
}
});

if (!result) {
throw new Exception("没得权限");
}
return result;
}
else {
System.out.println(
"缓存中没有找到权限数据");
return false;
}
}

private <T extends Annotation> T getAnnotation(HandlerMethod handlerMethod,
Class
<T> clazz) {
T annotation
= handlerMethod.getMethodAnnotation(clazz);
if (annotation != null) {
return annotation;
}
annotation
= AnnotationUtils.findAnnotation(handlerMethod.getBeanType(), clazz);
return annotation;
}

}

HandlerInterceptor用于拦截,如果有多个的时候,只有上一个返回true才会继续下一个的执行

 原理很简单,就是根据xml中配置,当其扫描路径下的方法被执行时,检查其上有个没有定义的注解,如果没有放行,如果有就根据逻辑判断确定返回true或者false

true表示验证成功

false表示验证失败

 

同理,我们还可以自定义jsp标签在页面上使用,如有权限才显示相应的东西,前后端必须一同控制才行,如果只前端判断后端不处理会被绕过,而只后台判断前端不处理又不友好

直接贴代码了

1.自定义标签的实现类,很简单,继承TagSupport

package net.zicp.xiaochangwei.web.tag;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.TagSupport;

import net.zicp.xiaochangwei.web.entity.Permission;
import net.zicp.xiaochangwei.web.entity.Role;
import net.zicp.xiaochangwei.web.utils.Constant;
import net.zicp.xiaochangwei.web.utils.SpringBeanUtil;

import org.apache.commons.collections4.IterableUtils;
import org.apache.commons.collections4.Predicate;
import org.springframework.data.redis.core.RedisTemplate;

import com.alibaba.fastjson.JSON;

/**
*
@author 肖昌伟 E-mail:317409898@qq.com
*
@version 创建时间:2016年9月18日 上午11:45:06
*
*/
public class HasPermissionTag extends TagSupport {
private static final long serialVersionUID = 1L;

private RedisTemplate<String, String> redisTemplate;

String name
= null;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int doStartTag() throws JspException {
try {
return isPermitted();
}
catch (Exception e) {
e.printStackTrace();
}
return TagSupport.SKIP_BODY;
}

public int isPermitted() throws Exception{
String p
= getName();
boolean show = checkPermission(p);
if (show) {
return TagSupport.EVAL_BODY_INCLUDE;
}
else {
return TagSupport.SKIP_BODY;
}
}

@SuppressWarnings(
"unchecked")
public boolean checkPermission(final String permissionCode)
throws Exception {
redisTemplate
= (RedisTemplate<String, String>) SpringBeanUtil.getBean("redisCache");
String cachedRole
= redisTemplate.opsForValue().get(Constant.ROLE+"1");
if (cachedRole != null) {
System.out.println(
"缓存中找到权限数据");
Role jrole
= JSON.parseObject(cachedRole, Role.class);
boolean result = IterableUtils.matchesAny(jrole.getPermissions(),
new Predicate<Permission>() {
public boolean evaluate(Permission p) {
return permissionCode.equals(p.getPermission());
}
});

if (!result) {
throw new Exception("没得权限");
}
return result;
}
else {
System.out.println(
"缓存中没有找到权限数据");
return false;
}
}
}

2.定义tld文件,直接放在WEB-INF下即可,其它路径请在web.xml中配置好

<?xml version="1.0" encoding="UTF-8" ?>

<taglib xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi
="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation
="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"
version
="2.0">
<description>权限自定义标签库</description>
<tlib-version>1.0</tlib-version>
<short-name>PermissionTagLibrary</short-name>
<uri>http://xiaochangwei.com/tags</uri>
<tag>
<description>这个标签的作用是用来判断有没有权限</description>
<name>hasPermission</name>
<!-- 标签对应的处理器类-->
<tag-class>net.zicp.xiaochangwei.web.tag.HasPermissionTag</tag-class>
<body-content>JSP</body-content>
<attribute>
<name>name</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>

</taglib>

3.页面使用

<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding
="UTF-8"%>
<%@ taglib uri
="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@ taglib uri
="http://xiaochangwei.com/tags" prefix="permission" %>
<!DOCTYPE html PUBLIC
"-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<link rel
="shortcut icon" href="/favicon.ico" />
<meta http-equiv
="Content-Type" content="text/html; charset=UTF-8">
<title>网站首页</title>
</head>
<body>

<permission:hasPermission name
="viewInfo">有权限<br/><br/></permission:hasPermission>