您好,登錄后才能下訂單哦!
實現功能權限校驗的功能有多種方法,其一使用攔截器攔截請求,其二是使用AOP拋異常。
首先用攔截器實現未登錄時跳轉到登錄界面的功能。注意這里沒有使用AOP切入,而是用攔截器攔截,因為AOP一般切入的是service層方法,而攔截器是攔截控制器層的請求,它本身也是一個處理器,可以直接中斷請求的傳遞并返回視圖,而AOP則不可以。
1.使用攔截器實現未登錄時跳轉到登錄界面的功能
1.1 攔截器SecurityInterceptor
package com.jykj.demo.filter; import java.io.PrintWriter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import com.alibaba.fastjson.JSON; import com.jykj.demo.util.Helper; import com.jykj.demo.util.Result; public class SecurityInterceptor implements HandlerInterceptor{ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("SecurityInterceptor:"+request.getContextPath()+","+request.getRequestURI()+","+request.getMethod()); HttpSession session = request.getSession(); if (session.getAttribute(Helper.SESSION_USER) == null) { System.out.println("AuthorizationException:未登錄!"+request.getMethod()); if("POST".equalsIgnoreCase(request.getMethod())){ response.setContentType("text/html; charset=utf-8"); PrintWriter out = response.getWriter(); out.write(JSON.toJSONString(new Result(false,"未登錄!"))); out.flush(); out.close(); }else{ response.sendRedirect(request.getContextPath()+"/login"); } return false; } else { return true; } } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { // TODO Auto-generated method stub } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { // TODO Auto-generated method stub } }
1.2.spring-mvc.xml(攔截器配置部分)
<!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory --> <mvc:resources mapping="/resources/**" location="/resources/" /> <mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/*"/> <!-- 攔截/ /test /login 等等單層結構的請求 --> <mvc:mapping path="/**/*.aspx"/><!-- 攔截后綴為.aspx的請求 --> <mvc:mapping path="/**/*.do"/><!-- 攔截后綴為 .do的請求 --> <mvc:exclude-mapping path="/login"/> <mvc:exclude-mapping path="/signIn"/> <mvc:exclude-mapping path="/register"/> <bean class="com.jykj.demo.filter.SecurityInterceptor"> </bean> </mvc:interceptor> </mvc:interceptors>
這里需要特別說明:攔截器攔截的路徑最好是帶有后綴名的,否則一些靜態的資源文件不好控制,也就是說請求最好有一個統一的格式如 .do 等等,這樣匹配與過濾速度會非常快。如果不這樣,例如 用 /** 來攔截所有的請求,則頁面渲染速度會非常慢,因為資源文件也被攔截了。
2.使用AOP實現功能權限校驗
對于功能權限校驗也可以類似地用攔截器來實現,只不過會攔截所有的請求,對不需要權限校驗的請求沒有很好的過濾功能,所以采用AOP指定攔截需要校驗的方法的方式來實現之。
2.1 切面類 PermissionAspect
package com.jykj.demo.filter; import java.io.IOException; import java.lang.reflect.Method; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.beans.factory.annotation.Autowired; import com.jykj.demo.annotation.ValidatePermission; import com.jykj.demo.exception.AccessDeniedException; import com.jykj.demo.service.SysUserRolePermService; /** * 事件日志 切面,凡是帶有 @ValidatePermission 以及@ResponseBody注解 控制器 都要進行 功能權限檢查, * 若無權限,則拋出AccessDeniedException 異常,該異常將請求轉發至一個控制器,然后將異常結果返回 * @author Administrator * */ public class PermissionAspect { @Autowired SysUserRolePermService sysUserRolePermService; public void doBefore(JoinPoint jp) throws IOException{ System.out.println( "log PermissionAspect Before method: " + jp.getTarget().getClass().getName() + "." + jp.getSignature().getName()); Method soruceMethod = getSourceMethod(jp); if(soruceMethod!=null){ ValidatePermission oper = soruceMethod.getAnnotation(ValidatePermission.class); if (oper != null) { int fIdx = oper.idx(); Object[] args = jp.getArgs(); if (fIdx>= 0 &&fIdx<args.length){ int functionId = (Integer) args[fIdx]; String rs = sysUserRolePermService.permissionValidate(functionId); System.out.println("permissionValidate:"+rs); if(rs.trim().isEmpty()){ return ;//正常 } } } } throw new AccessDeniedException("您無權操作!"); } private Method getSourceMethod(JoinPoint jp){ Method proxyMethod = ((MethodSignature) jp.getSignature()).getMethod(); try { return jp.getTarget().getClass().getMethod(proxyMethod.getName(), proxyMethod.getParameterTypes()); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (SecurityException e) { e.printStackTrace(); } return null; } }
2.2自定義注解ValidatePermission
package com.jykj.demo.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * @Descrption該注解是標簽型注解,被此注解標注的方法需要進行權限校驗 */ @Target(value = ElementType.METHOD) @Retention(value = RetentionPolicy.RUNTIME) @Documented public @interface ValidatePermission { /** * @Description功能Id的參數索引位置 默認為0,表示功能id在第一個參數的位置上,-1則表示未提供,無法進行校驗 */ int idx() default 0; }
說明: AOP切入的是方法,不是某個控制器請求,所以不能直接返回視圖來中斷該方法的請求,但可以通過拋異常的方式達到中斷方法執行的目的,所以在before通知中,如果通過驗證直接return返回繼續執行連接點方法,否則拋出一個自定義異常AccessDeniedException來中斷連接點方法的執行。該異常的捕獲可以通過系統的異常處理器(可以看做控制器)來捕獲并跳轉到一個視圖或者一個請求。這樣就達到攔截請求的目的。所以需要配置異常處理器。
2.3 spring-mvc.xml(異常處理器配置,以及aop配置)
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"> <!-- <property name="defaultErrorView" value="rediret:/error"></property> --> <property name="exceptionMappings"> <props> <!--<prop key="com.jykj.demo.exception.AuthorizationException">redirect:/login</prop>--> <prop key="com.jykj.demo.exception.AccessDeniedException">forward:/accessDenied</prop> </props> </property> </bean> <bean id="aspectPermission" class="com.jykj.demo.filter.PermissionAspect" /> <!-- 對帶有@ValidatePermission和ResponseBody注解的controller包及其子包所有方法執行功能權限校驗 --> <aop:config proxy-target-class="true"> <aop:aspect ref="aspectPermission"> <aop:pointcut id="pc" expression="@annotation(com.jykj.demo.annotation.ValidatePermission) and @annotation(org.springframework.web.bind.annotation.ResponseBody) and execution(* com.jykj.demo.controller..*.*(..)) " /> <aop:before pointcut-ref="pc" method="doBefore"/> </aop:aspect> </aop:config>
2.4 注解需要進行功能校驗的控制器請求
@RequestMapping(value = "/moduleAccess.do", method = RequestMethod.POST, produces="text/html;charset=utf-8") @ResponseBody @ValidatePermission public String moduleAccess(int fid,String action,FrmModule module) { System.out.println("fid:"+fid+",action:"+action); int rs = -1; try{ if(Helper.F_ACTION_CREATE.equals(action)){ rs = moduleService.access(module,Helper.DB_ACTION_INSERT); //module.setModuleid(rs); module = moduleService.selectByPrimaryKey(rs); }else if(Helper.F_ACTION_EDIT.equals(action)){ rs = moduleService.access(module,Helper.DB_ACTION_UPDATE); module = moduleService.selectByPrimaryKey(module.getModuleid()); }else if(Helper.F_ACTION_REMOVE.equals(action)){ rs = moduleService.access(module,Helper.DB_ACTION_DELETE); }else{ return JSON.toJSONString(new Result(false,"請求參數錯誤:action")); } }catch(Exception e){ e.printStackTrace(); return JSON.toJSONString(new Result(false,"操作失敗,出現異常,請聯系管理員!")); } if(rs<0){ return JSON.toJSONString(new Result(false,"操作失敗,請聯系管理員!")); } return JSON.toJSONString(new Result(true,module)); }
2.5 異常處理器將請求轉發到的控制器請求 forward:/accessDenied
@RequestMapping(value = "/accessDenied",produces = "text/html;charset=UTF-8") @ResponseBody public String accessDenied(){ return JSON.toJSONString(new Result(false,"您沒有權限對此進行操作!")); }
2.6 請求校驗不通過時 由上述的控制器返回 結果本身
如下所示:
{"info":"您沒有權限對此進行操作!","success":false}
2.7 功能校驗service 示例
/** * 校驗當前用戶在某個模塊的某個功能的權限 * @param functionId * @return 空字符串表示 有權限 ,否則是錯誤信息 * @throws Exception */ public String permissionValidate(int functionId){ Object o = request.getSession().getAttribute(Helper.SESSION_USER); //if(o==null) throw new AuthorizationException(); SysUser loginUser= (SysUser)o; if(loginUser.getUserid() == 1) return ""; try{ return mapper.permissionValidate(loginUser.getUserid(),functionId); }catch(Exception ex){ ex.printStackTrace(); return "數據庫操作出現異常!"; } }
說明: 這里僅僅是對帶有@ValidatePermission和@ResponseBody注解的controller包及其子包所有方法進行切入,這樣肯定是不夠通用的,應該是對帶有@ValidatePermission的方法進行切入,在切面類中通過判斷該方法是否有@ResponseBody注解來拋出不一樣的異常,若帶有@ResponseBody注解則拋出上述的異常返回json字符串,
否則,應該拋出另一個自定義異常然后將請求重定向到一個合法的視圖如error.jsp .
通過客戶端發送 /moduleAccess.do 請求,該請求對應的方法同時具有@ValidatePermission和@ResponseBody,并且有功能Id參數fid,這樣AOP可以切入該方法,執行doBefore通知,通過功能參數fid,對它結合用戶id進行權限校驗,若校驗通過直接返回,程序繼續執行,否則拋出自定義異常AccessDeniedException,該異常由系統捕獲(需要配置異常處理器)并發出請求 forward:/accessDenied ,然后對應的控制器 /accessDenied 處理該請求返回一個包含校驗失敗信息的json給客戶端。這樣發送 /moduleAccess.do 請求,如果校驗失敗,轉發到了/accessDenied請求,否則正常執行。繞了這么一個大圈子才實現它。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持億速云。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。