Spring AOP 实现功能权限校验功能
版权声明:本文为博主原创文章,未经博主允许不得转载。
实现功能权限校验的功能有多种方法,其一使用拦截器拦截请求,其二是使用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.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}
- 1
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请求,否则正常执行。绕了这么一个大圈子才实现它。
Spring AOP 实现功能权限校验功能的更多相关文章
- 基于Spring Aop实现类似shiro的简单权限校验功能
在我们的web开发过程中,经常需要用到功能权限校验,验证用户是否有某个角色或者权限,目前有很多框架,如Shiro Shiro有基于自定义登录界面的版本,也有基于CAS登录的版本,目前我们的系统是基于C ...
- Spring AOP 实现写事件日志功能
什么是AOP?AOP使用场景?AOP相关概念?Spring AOP组件?如何使用Spring AOP?等等这些问题请参考博文:Spring AOP 实现原理 下面重点介绍如何写事件日志功能,把日志保存 ...
- 使用AOP+自定义注解完成spring boot的接口权限校验
记使用AOP+自定义注解完成接口的权限校验,代码如下: pom文件添加所需依赖: 1 <dependency> 2 <groupId>org.aspectj</group ...
- 从零开始学 Java - Spring AOP 实现用户权限验证
每个项目都会有权限管理系统 无论你是一个简单的企业站,还是一个复杂到爆的平台级项目,都会涉及到用户登录.权限管理这些必不可少的业务逻辑.有人说,企业站需要什么权限管理阿?那行吧,你那可能叫静态页面,就 ...
- 基于Spring AOP实现的权限控制
1.AOP简介 AOP,面向切面编程,往往被定义为促使软件系统实现关注点的分离的技术.系统是由许多不同的组件所组成的,每一个组件负责一块特定的功能.除了实现自身核心功能之外,这些组件还经常承担着额外的 ...
- 使用Spring AOP来进行权限验证
使用Spring AOP前需要先引入相应的包 <dependency> <groupId>org.aspectj</groupId> <artifactId& ...
- SpringBoot aop 注解 数据权限校验
注解类: @Retention(RetentionPolicy.RUNTIME) public @interface DataAuthValid { //位置 public int index() d ...
- Spring MVC 使用拦截器优雅地实现权限验证功能
在上一篇 SpringAOP 实现功能权限校验功能 中虽然用AOP通过抛异常,请求转发等勉强地实现了权限验证功能,但感觉不是那么完美,应该用拦截器来实现才是最佳的,因为拦截器就是用来拦截请求的,在请求 ...
- [转]彻底征服 Spring AOP 之 实战篇
Spring AOP 实战 看了上面这么多的理论知识, 不知道大家有没有觉得枯燥哈. 不过不要急, 俗话说理论是实践的基础, 对 Spring AOP 有了基本的理论认识后, 我们来看一下下面几个具体 ...
随机推荐
- TopCoder SRM 582 ColorfulBuilding
DP 思路是三维,但是时间肯定会超时,需要根据其特殊性质加两个标记数组,优化成二维. 刚开始想了N久N久,没感觉,还是动手画了一下才有用呀,意淫再久,不如动手呀. 代码: #include<i ...
- ECMAScript 6新特性(1)数组篇
数组现有的方法: .concat():连接两个或更多的数组,并返回结果. .join():把数组的所有元素放入一个字符串.元素通过指定的分隔符进行分隔. .pop():删除并返回数组的最后一个元素 . ...
- IT公司100题-2-设计带min函数的stack
问题描述: 定义栈的数据结构,要求添加一个min 函数,能够得到栈的最小元素. 要求函数min.push 以及pop 的时间复杂度都是O(1). 双倍空间实现: 保存2个栈,分别是元素和当前最小值 ...
- [vijos P1512] SuperBrother打鼹鼠
这周好好码树状数组和线段树!!之前没写过二维树状数组,凭借一维的思路居然写了个比较像模像样的东西出来,原来我没那么脑残.唯一要注意的就是getsum四个矩形加减的边界条件,这里看了别人标程才意识到错误 ...
- 红帽中出现”This system is not registered with RHN”的解决方案
原因是你的linux没有在红帽网络上注册,所以无法下载上面的软件包,替代方案可以使用centos. 下面介绍下使用centos 的流程 1.卸载rhel的默认安装的yum包查看yum包rpm -qa| ...
- C++ primer的第一章的主要内容
第一章主要是把C++的主要的部分简单的介绍了一下,让读者对C++开始有一个简单的了解.看完第一章的收获就是知道如何去读入不确定数目的输入,主要是形式是:whlie(cin>>s){},利用 ...
- HTML--11marquee标签
页面的自动滚动效果,可由javascript来实现, 但是有一个html标签 - <marquee></marquee>可以实现多种滚动效果,无需js控制. 使用marquee ...
- win10 mac地址修改器
NoVirus Thanks MAC Address Changer NoVirus Thanks MAC Address Changer is yet another simple MAC addr ...
- PHP图像处理之画图
PHP图像处理 画图 验证码,统计图 安装GD库-----LAMP 安装后在D:\APMServ5.2.6\PHP\ext文件中有php_gd2.dll文件 ...
- ERP反馈信息管理(十九)
前台显示的界面: <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Custo ...