今天发现一个问题:使用shiro的时候,虽然隐藏掉了一些菜单,但是当我们通过get请求直接访问菜单的时候还是会访问到,也就是shiro可以在界面实现隐藏一些信息,但是没有真正的根据权限码验证请求,于是自己在后台实现验证。
需求:有权限(权限码是systemmanager:settings)的人可以点击系统设置跳转到系统设置页面,没权限的人看不到菜单,但是通过get访问可以访问到,于是需要在后台拦截。
实现思路:在需要精确验证的方法开始先验证权限,如果验证成功啥也不做,验证失败的话就抛出一个没有权限的异常。在拦截器中捕捉到异常就记录日志,并返回到提醒页面。
1. 验证Shiro权限的工具类(此工具还可以进一步完善,封装为判断是否有指定角色,或者有任意角色)
package cn.xm.exam.utils; import org.apache.shiro.SecurityUtils; import org.apache.shiro.subject.Subject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import cn.xm.exam.bean.system.User; import cn.xm.exam.exception.NoPermissionException; /** * 验证shiro权限的工具类 * * @author QiaoLiQiang * @time 2018年11月3日下午9:30:46 */ public class ShiroPermissionUtils { private static final Logger log = LoggerFactory.getLogger(ShiroPermissionUtils.class); private ShiroPermissionUtils() { } /** * 检查当前用户是否有权限(任意一项) * * @param permissionCodes * 任意权限 * @throws NoPermissionException */ public static void checkPerissionAny(String... permissionCodes) { if (permissionCodes == null || permissionCodes.length == 0) { return; } // 获取用户信息 Subject currentUser = SecurityUtils.getSubject(); for (String permission : permissionCodes) { boolean permitted = currentUser.isPermitted(permission);// 判断是否有权限 if (permitted) { return; } } // 没权限就抛出一个异常 Object principal = currentUser.getPrincipal(); if (principal instanceof User) { User user = (User) principal; log.error("user {} no permission !", user.getUsername()); } throw new NoPermissionException("no permission "); } /** * 检查当前用户是否有权限(所有的) * * @param permissionCodes * 任意权限 * @throws NoPermissionException */ public static void checkPerissionAll(String... permissionCodes) { if (permissionCodes == null || permissionCodes.length == 0) { return; } // 获取用户信息 Subject currentUser = SecurityUtils.getSubject(); for (String permission : permissionCodes) { boolean permitted = currentUser.isPermitted(permission);// 判断是否有权限 if (!permitted) { // 没权限就抛出一个异常 Object principal = currentUser.getPrincipal(); if (principal instanceof User) { User user = (User) principal; log.error("user {} no permission !", user.getUsername()); } throw new NoPermissionException("no permission "); } } } }
解释:
(1)Subject currentUser = SecurityUtils.getSubject(); 先获取到Subject,
(2)boolean permitted = currentUser.isPermitted(permission); 然后验证权限
(3)如果验证失败就记录日志,(获取到subject中的principal,principal其实是认证的时候装进SimpleAuthenticationInfo的user对象)
Object principal = currentUser.getPrincipal();
if (principal instanceof User) {
User user = (User) principal;
log.error("user {} no permission !", user.getUsername());
}
throw new NoPermissionException("no permission ");
认证时候装进去的User对象:---principal
2.自定义异常
必须继承RuntimeException,运行时异常不需要在抛出异常的时候显示的捕捉或者声明(throws.....),如果继承Exception为检查异常,需要捕捉或者throws声明
package cn.xm.exam.exception; /** * 没有权限异常 * * @author QiaoLiQiang * @time 2018年11月3日下午9:34:12 */ public class NoPermissionException extends RuntimeException { /** * */ private static final long serialVersionUID = -4442982597754920924L; public NoPermissionException(String msg) { super(msg); } }
3.测试Action
方法开始先验证权限,但是没有捕捉异常,如果验证失败异常会抛出在拦截器中被捕捉。
package cn.xm.exam.action.system; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Controller; import com.opensymphony.xwork2.ActionSupport; import cn.xm.exam.utils.ShiroPermissionUtils; @Controller @Scope("prototype") public class SettingsAction extends ActionSupport { /** * serial */ private static final long serialVersionUID = -5885555441378384728L; private static final Logger log = LoggerFactory.getLogger(ShiroPermissionUtils.class); public String settings() { ShiroPermissionUtils.checkPerissionAny("systemmanager:settings"); return "settings"; } }
4.struts全局异常拦截器
捕捉到NoPermissionException异常就返回noPermissionError在全局结果集中处理
package cn.xm.exam.interceptor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.opensymphony.xwork2.ActionInvocation; import com.opensymphony.xwork2.interceptor.Interceptor; import cn.xm.exam.exception.NoPermissionException; public class ExceptionInterception implements Interceptor { private static final Logger log = LoggerFactory.getLogger(ExceptionInterception.class); /** * */ private static final long serialVersionUID = 2268867259828199826L; @Override public void destroy() { } @Override public void init() { } @Override public String intercept(ActionInvocation arg0) throws Exception { log.info("enter ExceptionInterception intercept ... "); String result = ""; try { result = arg0.invoke(); log.info("result -> {}", result); } catch (NoPermissionException e) { log.error("no permission", e); return "noPermissionError"; } catch (Throwable e) { log.error("未处理的异常在拦截器被拦截,class:{}", arg0.getAction().getClass(), e); return "interceptorError"; } log.debug("exit ExceptionInterception intercept ... "); return result; } }
5.Struts.xml配置全局异常错误界面:
返回值是noPermissionError跳转到noPermissionError.jsp页面
<package name="interceptPackage" extends="json-default"> <!-- 拦截器 --> <interceptors> <!-- 定义刚才的拦截器 --> <interceptor name="exceptionInterceptor" class="cn.xm.exam.interceptor.ExceptionInterception"></interceptor> <!-- 定义拦截器栈 --> <interceptor-stack name="myStack"> <!-- 拦截器栈里面可以引用另外一个拦截器,也可以引用另外一个拦截器栈 --> <interceptor-ref name="defaultStack"></interceptor-ref> <interceptor-ref name="exceptionInterceptor"></interceptor-ref> </interceptor-stack> </interceptors> <!-- 这句是设置所有Action自动调用的拦截器堆栈 --> <default-interceptor-ref name="myStack" /> <!-- 拦截器拦截的全局异常 --> <global-results> <result name="interceptorError">/interceptorError.jsp</result> <result name="noPermissionError">/noPermissionError.jsp</result> </global-results> </package>
6.noPermissionError.jsp页面
给出没权限提醒。
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <% String path = request.getContextPath(); String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/"; %> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>错误提醒</title> </head> <body> <br /> <span style="font-weight: bold; font-size: 20px; margin: 20px;">对不起,您没有权限访问该请求!</span> <br /> </body> </html>
7.测试:
至此,完成了后台验证,也就是在所有方法开始先验证权限。
还有另一种办法就是自定义注解实现权限验证,有点类似于shiro自带的注解验证权限,参考:https://www.cnblogs.com/qlqwjy/p/7257616.html
原文地址:https://www.cnblogs.com/qlqwjy/p/9902711.html