基于拦截器和注解实现页面的访问权限控制
在 web 系统中,经常需要对每个页面的访问进行权限控制。譬如,要进入 xx 公司的开放 平台, isv 需要注册成为开发者,开发者的状态有审核中、有效、冻结、拒绝、删除等状态,然后根据不同的状态,开发者可以访问不同的页面。只有有效或冻结状态可以访问只读功能的页面(即该页面的访问不会造成后台数据的变化),只有有效状态可以访问具有写功能的页面。
如何实现该访问控制的需求呢?最直观的做法就是:在每个页面对应的 controller 里,都去调用查询开发者的服务,然后判断开发者的状态。
@Controller @RequestMapping("/appDetail.htm") public class AppDetailController { @RequestMapping(method = RequestMethod.GET) public String doGet(ModelMap modelMap, HttpServletRequest httpServletRequest) { //1. 开发者有效性判断 Developer developer = developerManageServiceClient .getByCardNo(cardNo); if (null == developer){ return ERROR_VM; } if (DeveloperStatus.VALID != developer.getStatus() && DeveloperStatus.FREEZE != developer.getStatus()) { return ERROR_VM; } //2. 业务操作,此处省略 } } @Controller @RequestMapping("/appBaseInfoEdit.htm") public class AppBaseInfoEditController { @RequestMapping(method = RequestMethod.POST) public String modify(ModelMap modelMap, HttpServletRequest httpServletRequest, AppBaseInfoForm appBaseInfoForm) { //1. 开发者有效性判断 Developer developer = developerManageServiceClient .getByCardNo(cardNo); if (null == developer){ return ERROR_VM; } if (DeveloperStatus.VALID != developer.getStatus()) { return ERROR_VM; } //2. 业务操作,此处省略 } }
appDetail.htm 对应的页面需要开发者的状态为有效或者冻结, appBaseInfoEdit.htm 对应的页面需要开发者的状态为有效。
采用这种方式有以下缺点:
- 多个 controller 里实现同样的代码,造成代码冗余;
- 对于每个 controller 的主体功能来说,对开发者状态的检查是一个横切关注点,将这种关注点掺和在主功能里,会使得主体业务逻辑不清晰。
所以,可以基于AOP 将这种横切关注点以拦截器的方式实现,但存在的一个问题是,拦截器如何知道某个页面的访问对开发者状态的要求呢?可以基于注解实现。譬如:
@Controller @RequestMapping("/appDetail.htm") @Permission(permissionTypes = { PermissionEnum.DEVELOPER_VALID }) public class AppDetailController { @RequestMapping(method = RequestMethod.GET) public String doGet(ModelMap modelMap, HttpServletRequest httpServletRequest) { //1. 业务操作,此处省略 } } @Controller @RequestMapping("/appBaseInfoEdit.htm") @Permission(permissionTypes = { PermissionEnum.DEVELOPER_VALID, PermissionEnum.DEVELOPER_FREEZE }) public class AppBaseInfoEditController { @RequestMapping(method = RequestMethod.POST) public String modify(ModelMap modelMap, HttpServletRequest httpServletRequest, AppBaseInfoForm appBaseInfoForm) { //1. 业务操作,此处省略 } }
@Permission(permissionTypes = { PermissionEnum.DEVELOPER_VALID }) ,表示开发者的状态必须是有效; @Permission(permissionTypes = { PermissionEnum.DEVELOPER_VALID, PermissionEnum.DEVELOPER_FREEZE }) ,表示开发者的状态必须是有效或者冻结。这样,每个 controller 的主体业务逻辑就清晰了。
下面分析一下注解和拦截器是如何实现的:
注解实现:
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface Permission { /** 检查项枚举 */ PermissionEnum[] permissionTypes() default {}; /** 检查项关系 */ RelationEnum relation() default RelationEnum.OR; }
RelationEnum 该枚举表示各检查项( permissionTypes )之间的关系, OR 表示至少需要满足其中一个检查项, AND 表示需要满足所有检查项
/** * 权限检查拦截器 * * @author xianwu.zhang * @version $Id: PermissionCheckInterceptor.java, v 0.1 2012-10-25 下午07:48:11 xianwu.zhang Exp $ */ public class PermissionCheckInterceptor extends HandlerInterceptorAdapter { /** 权限检查服务 */ private PermissionCheckProcessor permissionCheckProcessor; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { Class<?> clazz = handler.getClass(); if (clazz.isAnnotationPresent(Permission.class)) { Permission permission = (Permission) clazz.getAnnotation(Permission.class); return permissionCheckProcessor.process(permission, request,response); } return true; } }
* 权限检查器 * @author xianwu.zhang * @version $Id: PermissionCheckProcessor.java, v 0.1 2012-11-5 下午05:13:17 xianwu.zhang Exp $ */ public class PermissionCheckProcessor { public boolean process(Permission permission, HttpServletRequest request, HttpServletResponse response) { PermissionEnum[] permissionTypes = permission.permissionTypes(); try { String cardNo = OperationContextHolder.getPrincipal().getUserId(); HttpSession session = request.getSession(false); If(null != session){ //查询开发者 Developer developer = developerManageServiceClient .getByCardNo(cardNo); if (null != developer && checkPermission(permissionTypes, permission.relation(),developer .getStatus())) { return true; } } sendRedirect(response, ISV_APPLY_URL); return false; } catch (Exception e) { sendRedirect(response, ISV_APPLY_URL); return false; } } //省略 } private void sendRedirect(HttpServletResponse response, String redirectURI) { URIBroker uriBroker = uriBrokerManager.getUriBroker(redirectURI); String url = uriBroker.render(); try { response.sendRedirect(url); } catch (IOException e) { logger.error("转向页面:" + url + "跳转出错:", e); } } }
Xml 配置如下:
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"> <property name="interceptors"> <list> <ref bean="permissionCheckInterceptor" /> </list> </property> </bean> <bean id="permissionCheckInterceptor" class="com.xxx.xxx.web.home.interceptor.PermissionCheckInterceptor" /> <bean id="permissionCheckProcessor" class="com.xxx.xxx.web.home.interceptor.PermissionCheckProcessor" />
还有一个小细节可以优化一下,现在是每访问一个页面,都会经过这个拦截器,拦截器里面都有一次 webservice 调用以查询开发者信息(开发者 cardNo 和开发者状态)。但真实场景中, 99.99% 的情况是,用户在同一个 session 下完成所有的业务,即访问的所有页面都具有同一个 sessison ,因此可以在开发者第一次访问页面时,通过 webservice 调用查询到开发者信息,如果权限校验通过,则将开发者 cardNo 和开发者状态放在 session 里,那么在 session 未失效前, 开发者再次访问其他页面时,可以在拦截器里先判断目前登陆的用户卡号是否与 session 里存储的 cardNo 相同,如果相同,则就不需要再调用 webserivce 了,可以直接取 session 里存储的开发者状态来进行权限校验,这样就减少了大量的不必要的 webservice 调用。
/** * 权限检查器 * @author xianwu.zhang * @version $Id: PermissionCheckProcessor.java, v 0.1 2012-11-5 下午05:13:17 xianwu.zhang Exp $ */ public class PermissionCheckProcessor { public boolean process(Permission permission, HttpServletRequest request, HttpServletResponse response) { PermissionEnum[] permissionTypes = permission.permissionTypes(); try { String cardNo = OperationContextHolder.getPrincipal().getUserId(); HttpSession session = request.getSession(false); if (null != session) { String developerCardNo = (String) session.getAttribute("developerCardNo"); if (StringUtil.isNotBlank(cardNo) && StringUtil.equals(cardNo, developerCardNo)) { String status = (String) session.getAttribute("status"); if (checkPermission(permissionTypes, permission.relation(), status)) { return true; } } else { Developer developer = developerManageServiceClient .getByCardNo(cardNo); if (null != developer && checkPermission(permissionTypes, permission.relation(), developer .getStatus())) { session.setAttribute("status", developer.getStatus()); session.setAttribute("developerCardNo ", cardNo); return true; } } } sendRedirect(response, ISV_APPLY_URL); return false; } catch (Exception e) { sendRedirect(response, ISV_APPLY_URL); return false; } }
本文为原创,转载请注明出处
相关推荐
在这个主题中,“基于Shiro拦截URL,实现权限控制”意味着我们将探讨如何利用Shiro来管理应用程序中的访问权限,确保用户只能访问他们被授权的资源。 首先,我们需要理解Shiro的三个核心概念: 1. 身份验证...
Struts2 框架是 SSH2 架构的重要组成部分,提供了一种强大的MVC(模型-视图-控制器)设计模式实现。...这种基于拦截器的登录验证方法是 SSH2 框架中常见且实用的实践,对于开发大型企业级应用非常有价值。
3. **注解方式**:除了XML配置,Struts2也支持使用注解在Action类或方法上直接声明拦截器,这样可以更方便地控制权限。 4. **共享数据**:在拦截器中,可以利用ActionContext或ThreadLocal等机制来传递和共享数据,...
在本项目中,我们主要探讨的是一个基于SpringMVC、Mybatis、Mysql数据库的完整应用实例,同时涉及到了权限管理和拦截器的实现。这个实例提供了详细的源代码,包括SQL脚本,以及一些实用的工具类,使得开发者可以快速...
- 使用Java配置时,可以通过`@EnableWebMvc` 注解启用Web MVC配置,并在`WebMvcConfigurer` 实现类中重写`addInterceptors()` 方法来注册拦截器。 3. **自定义拦截器**: - 自定义拦截器需要继承`...
4. **拦截器设置**: 使用Shiro的Filter链来拦截所有请求,进行权限检查。根据URI判断用户是否有访问权限。 5. **Vue前端交互**: Vue组件可以调用后端提供的API获取当前用户的角色和权限,然后动态渲染菜单或按钮。...
一个常见的应用场景是登录验证拦截器,当用户访问受保护的资源时,拦截器首先检查用户是否已登录,未登录则重定向到登录页面。 ```java public class LoginInterceptor implements Interceptor { @Override ...
在这个主题“Android-基于Arouter的登录拦截”中,我们将深入探讨如何利用Arouter实现对特定页面的登录拦截机制,确保只有已登录的用户才能访问某些敏感或私密的功能。 首先,了解Arouter的基本概念是至关重要的。...
2. **权限校验**:如果拦截器检测到未登录的用户试图访问受保护的资源,如`userlogin.jsp`,它可以阻止请求并重定向到登录页面`login.jsp`。 3. **会话管理**:拦截器可以检查用户的会话状态,确保用户在会话过期后...
过滤器(Filter)技术常被用来实现多层权限控制,确保只有合法的用户才能访问特定的资源。本文将深入探讨如何利用过滤器实现多层权限控制,并结合给定的标签“源码”和“工具”,来提供一个具体的实践示例。 首先,...
为了实现基于方法级别的权限控制,我们需要定义一个自定义注解,用于标识哪些方法需要进行权限检查以及需要哪种权限。同时,还需要编写一个自定义拦截器来处理这些注解,并在运行时检查用户的权限。 **1. 自定义...
SpringMVC的Session拦截器是提高应用安全性、控制访问权限的有效工具。通过自定义拦截器,开发者可以在不修改业务逻辑的情况下,轻松地增加对Session的验证,确保每个请求都符合预期的上下文。在实际项目中,结合...
登录拦截器是Web应用中一种常见的安全控制手段,能够有效地控制用户访问权限,保护系统资源的安全。通过本文介绍的简单实现案例,我们不仅可以了解到登录拦截器的基本原理,还能掌握其实现过程中的关键步骤和技术...
Spring Boot基于Spring MVC,因此其拦截器机制与Spring MVC中的拦截器类似。下面我们将深入探讨Spring Boot拦截器的工作原理、实现方式以及应用场景。 1. **拦截器的概念** 拦截器是一种设计模式,它在目标方法...
在Java Web开发中,Spring MVC是一个非常流行的框架,它提供...通过拦截器,开发者能够实现诸如用户认证、权限控制等高级功能,提高Web应用的安全性和用户体验。理解并掌握这种技术对于任何Java Web开发者都是有益的。
综合以上,本文通过具体的代码示例和技术细节深入讲解了如何使用Spring MVC拦截器实现session控制,包括如何监听session的创建和销毁,如何实现用户的重复登录控制以及如何在拦截器中处理未登录用户的访问权限问题,...
在Java Web开发中,权限控制是一项至关重要的任务...综上所述,"java权限控制"涉及了Web应用中用户权限的管理和验证,主要通过过滤器来实现。理解并正确应用这些概念和技术对于构建安全、可靠的Java Web应用至关重要。
在“struts实现的权限分配”这一主题中,我们将深入探讨如何使用Struts来设计和实现一个安全的权限管理系统。 首先,我们需要理解权限分配的基本概念。在Web应用程序中,权限分配是确保不同用户或角色访问不同资源...
权限通常基于角色,例如“管理员”可以访问所有页面,而“普通用户”只能访问一部分。 3. **过滤器链(Filter Chain)**:Spring Security通过一系列过滤器来拦截HTTP请求,执行认证和授权操作。这些过滤器如`...
Struts拦截器是Java Web开发中的一个重要概念,尤其在基于MVC框架的Struts2中,它是实现业务逻辑控制和视图分离的关键组件。拦截器是AOP(面向切面编程)思想的一种体现,用于在动作执行前后进行额外的操作,如日志...