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.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请求,否则正常执行。绕了这么一个大圈子才实现它。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • spring aop 拦截业务方法,实现权限控制示例

    难点:aop类是普通的java类,session是无法注入的,那么在有状态的系统中如何获取用户相关信息呢,session是必经之路啊,获取session就变的很重要.思索很久没有办法,后来在网上看到了解决办法. 思路是: i. SysContext  成员变量 request,session,response ii. Filter 目的是给 SysContext 中的成员赋值 iii.然后在AOP中使用这个SysContext的值 要用好,需要理解  ThreadLocal和  和Filter

  • Java之Spring AOP 实现用户权限验证

    每个项目都会有权限管理系统 无论你是一个简单的企业站,还是一个复杂到爆的平台级项目,都会涉及到用户登录.权限管理这些必不可少的业务逻辑.有人说,企业站需要什么权限管理阿?那行吧,你那可能叫静态页面,就算这样,但你肯定也会有后台管理及登录功能. 每个项目中都会有这些几乎一样的业务逻辑,我们能不能把他们做成通用的系统呢? AOP 实现用户权限验证 AOP 在实际项目中运用的场景主要有权限管理(Authority Management).事务管理(Transaction Management).安全管

  • spring aop实现用户权限管理的示例

    AOP 在实际项目中运用的场景主要有 权限管理(Authority Management).事务管理(Transaction Management).安全管理(Security).日志管理(Logging)和调试管理(Debugging) 等. 问题源于项目开发 最近项目中需要做一个权限管理模块,按照之前同事的做法是在controller层的每个接口调用之前上做逻辑判断,这样做也没有不妥,但是代码重复率太高,而且是体力劳动,so,便有了如题所说的使用spring aop做一个切点来实现通用功能的

  • Spring AOP实现功能权限校验功能的示例代码

    实现功能权限校验的功能有多种方法,其一使用拦截器拦截请求,其二是使用AOP抛异常. 首先用拦截器实现未登录时跳转到登录界面的功能.注意这里没有使用AOP切入,而是用拦截器拦截,因为AOP一般切入的是service层方法,而拦截器是拦截控制器层的请求,它本身也是一个处理器,可以直接中断请求的传递并返回视图,而AOP则不可以. 1.使用拦截器实现未登录时跳转到登录界面的功能 1.1 拦截器SecurityInterceptor package com.jykj.demo.filter; import

  • vue路由权限校验功能的实现代码

    引言 做后台系统的时候,难免会有用户权限的判断.admin可以查看全部菜单,user只能查看部分菜单. 一开始接触这个需求的时候,完全是纯前端做的.在配置路由的时候,加一个roles的属性,通过判断用户的roles是否与路由的roles属性相匹配来作为显示隐藏的依据 { path: '/router', name: 'router', meta: { title: '标题', roles: ['admin','user'] }, component: index, children: [ { p

  • spring cloud oauth2 实现用户认证登录的示例代码

    需求 在微服务架构中,我们有很多业务模块,每个模块都需要有用户认证,权限校验.有时候也会接入来自第三方厂商的应用.要求是只登录一次,即可在各个服务的授权范围内进行操作.看到这个需求,立马就想到了这不就是单点登录吗?于是基于这样的需求,作者使用spring-cloud-oauth2去简单的实现了下用户认证和单点登录. 相关介绍 OAuth2 OAuth2是一个关于授权的网络标准,他定制了设计思路和执行流程.OAuth2一共有四种授权模式:授权码模式(authorization code).简化模式

  • 手写一个@Valid字段校验器的示例代码

    上次给大家讲述了 Springboot 中的 @Valid 注解 和 @Validated 注解的详细用法: 详解Spring中@Valid和@Validated注解用法 当我们用上面这两个注解的时候,需要首先在对应的字段上打上规则注解,类似如下. @Data public class Employee { /** 姓名 */ @NotBlank(message = "请输入名称") @Length(message = "名称不能超过个 {max} 字符", max

  • Spring Security OAuth2 实现登录互踢的示例代码

    本文主要介绍了Spring Security OAuth2 实现登录互踢的示例代码,分享给大家,具体如下: 背景说明 一个账号只能一处登录,类似的业务需求在现有后管类系统是非常常见的. 但在原有的 spring security oauth2 令牌方法流程(所谓的登录)无法满足类似的需求. 我们先来看 TokenEndpoint 的方法流程 客户端 带参访问 /oauth/token 接口,最后去调用 TokenGranter TokenGranter 根据不同的授权类型,获取用户认证信息 并去

  • SqlServer2000+ 身份证合法校验函数的示例代码

    下面看下sqlserver2000身份证校验的代码,具体代码如下所示: /* 身份校验行数 */ if exists(select * from sysobjects where name='fun_utils_idnumberoprater' and type='FN') drop function fun_utils_idnumberoprater go create function fun_utils_idnumberoprater ( @idnumber varchar(50)=''

  • spring中使用mybatis实现批量插入的示例代码

    有3种实现方式:foreach,spring事务,以及ExecutorType.BATCH. 1. foreach方式 这种方式实际是对SQL语句进行拼接,生成一个长长的SQL,对很多变量进行绑定.如果数据量不大(1000个以内),可以用这种方式.如果数据量太大,可能数据库会报错. 定义接口 public interface StudentMapper05 { public void insertStudent(List<Student> studentList); } 定义mapper 适用

  • Spring Cloud 系列之负载均衡 Ribbon的示例代码

    1.1 简介 1.1.1 概述   Ribbon 是 Netflix 发布的负载均衡器,它有助于控制 HTTP 和 TCP 客户端的行为.为 Ribbon 配置服务提供者地址列表后,Ribbon 就可基于某种负载均衡算法,自动地帮助服务消费者去请求.Ribbon 默认为我们提供了很多的负载均衡算法,例如轮询.随机等.当然,我们也可为 Ribbon 实现自定义的负载均衡算法.Ribbon 现在已经进入维护状态,但目前仍在大规模使用,Spring Cloud 准备使用 LoadBalancer 作为

  • Spring AOP + 注解实现统一注解功能

    1. 概述 在一般系统中,当我们做了一些重要的操作时,如登陆系统,添加用户,删除用户等操作时,我们需要将这些行为持久化.本文我们通过Spring AOP和Java的自定义注解来实现日志的插入.此方案对原有业务入侵较低,实现较灵活 2. 日志的相关类定义 我们将日志抽象为以下两个类:功能模块和操作类型 使用枚举类定义功能模块类型ModuleType,如学生.用户模块 public enum ModuleType { DEFAULT("1"), // 默认值 STUDENT("2

  • 使用Spring AOP实现用户操作日志功能

    目录 我使用Spring AOP实现了用户操作日志功能 需求分析 功能实现 1. 需要一张记录日志的 Log 表 导出的 sql 如下: 2.我使用的是 Spring Boot 所以需要引入 spring aop 的 starter 3.Log 实体类 4.ILog 注解 5.切面类 LogAspect 总结 我使用Spring AOP实现了用户操作日志功能 今天答辩完了,复盘了一下系统,发现还是有一些东西值得拿出来和大家分享一下. 需求分析 系统需要对用户的操作进行记录,方便未来溯源 首先想到

随机推荐