在進行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>
