Acegi + Spring + Hibernate + Struts 2搭建基于角色的权限控制系统

2024-01-15 19:58

本文主要是介绍Acegi + Spring + Hibernate + Struts 2搭建基于角色的权限控制系统,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

安全永远是WEB应用系统必须面对的头等大事, 也是最头疼的事, 其实安全系统就只包括两个问题: 认证和授权.

    以前做些网站系统, 安全检测逻辑都在放在须要安全控制的代码前面, 这样做有很多不好的地方, 重复多次的编码就不用说了, 代码移植性, 重用性都得不到体现, 安全检测逻辑要永远和业务逻辑放在一起.

    那么, 能不能够在进入方法前就调用一些安全检测? 其实Spring AOP就是这个思想, 那么又如何实现安全检测呢? Spring Acegi Security 框架就是做这个事情.

     本文主要是讨论下在已有的SSH系统中, 如何使用Acegi作为安全框架实现基于角色的权限控制(Role Based Access Control RBAC) , 本文主要是以Java 5注解的形式来配置安全框架, 大大减化配置和操作.

    本文的主要参考资料: <Spring 2.0 核心技术与最佳实践> 第10章 (Spring Acegi 安全框架)

                <精通Spring 2.X -- 企业应用开发详解> 第17章 (使用Acegi 实施应用系统安全)

                acegi-security-1.0.6 官方文档

    说明: 本文介绍的是RBAC, 在官方文档的基础上有所扩展或改动, 以更适合WEB应用系统. 其实我觉得大多数的网站基于角色已经足够了, 一般都没必要基于权限.

     文章开始:

一. 下载所要的软件或JAR包:

    我的相关配置是: Java 5, Tomcat 5.5.26, Struts 2.0.11, Spring 2.5.1, Hibernate 3.2, Acegi 1.0.6

二. 建立相关的数据库:

     数据表: 用户信息表User: id, enable, user_name, user_pass, email_box

                 角色信息表RoleInfo: id, role_name, role_title, descp

                 用户与角色关联表(用户与角色是多对多关系)UserRole: user_id, user_name, role_id, role_name

                并在这三个表中插入相关的数据, 我是定义了两种角色(role_name): ROLE_USER, ROLE_ADMIN

                和三个用户, 一个用户角色为: ROLE_USER, ROLE_ADMIN

                 另一个用户角色为: ROLE_USER

                第三个没有角色.

二. 修改配置文件:

    其实对Acegi框架的应用难点就在配置文件, 所以要特别注意了:

    在 src 建立Acegi的配置文件: acegi-security.xml 当然这个文件的名称是可以任意的.

    acegi-security.xml 说白了就是配置: 安全拦截器, 认证管理器, 决策管理器.

    其内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- ========================= 认证管理器 ========================= -->
<bean id="authenticationManager" class="org.acegisecurity.providers.ProviderManager">
<property name="providers">
   <list>
    <ref bean="daoAuthenticationProvider" />
    <ref bean="rememberMeAuthenticationProvider" />
   </list>
</property>
</bean>

<!-- 基于DAO验证的AuthenticationProvider -->
<bean id="daoAuthenticationProvider" class="org.acegisecurity.providers.dao.DaoAuthenticationProvider">
<property name="userDetailsService" ref="userDetailsService" />
</bean>
<bean id="userDetailsService" class="org.ymcn.security.AcegiUserDeitailsService">
<property name="userDao" ref="userDao" />
<property name="userRoleDao" ref="userRoleDao" />
</bean>

<bean id="rememberMeAuthenticationProvider" class="org.acegisecurity.providers.rememberme.RememberMeAuthenticationProvider">
<property name="key" value="obullxl@163.com" />
</bean>
<bean id="rememberMeServices" class="org.acegisecurity.ui.rememberme.TokenBasedRememberMeServices">
<property name="userDetailsService" ref="userDetailsService" />
<property name="parameter" value="j_remember_me" />
<property name="key" value="obullxl@163.com" />
<property name="tokenValiditySeconds" value="31536000" />
</bean>

<!-- ========================= 决策管理器 ========================= -->
<bean id="accessDecisionManager" class="org.acegisecurity.vote.AffirmativeBased">
<property name="decisionVoters">
   <list>
    <ref bean="roleVoter" />
   </list>
</property>
<!-- 是否全部弃权就通过 -->
<property name="allowIfAllAbstainDecisions" value="false" />
</bean>
<bean id="roleVoter" class="org.acegisecurity.vote.RoleVoter">
<property name="rolePrefix" value="ROLE_" />
</bean>

<!-- ========================= 过滤器链 ========================= -->
<bean id="filterChainProxy" class="org.acegisecurity.util.FilterChainProxy">
<property name="filterInvocationDefinitionSource">
   <value>
    CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
    PATTERN_TYPE_APACHE_ANT
    /**=httpSessionContextIntegrationFilter,logoutFilter,authenticationProcessingFilter,rememberMeFilter,exceptionFilter,securityInterceptor
   </value>
</property>
</bean>
<bean id="httpSessionContextIntegrationFilter" class="org.acegisecurity.context.HttpSessionContextIntegrationFilter" />
<bean id="logoutFilter" class="org.acegisecurity.ui.logout.LogoutFilter">
<!-- 登录退出后的URL -->
<constructor-arg value="/" />
<constructor-arg>
   <list>
    <ref bean="rememberMeServices" />
    <bean class="org.acegisecurity.ui.logout.SecurityContextLogoutHandler" />
   </list>
</constructor-arg>
<!-- 登录退出的URL -->
<property name="filterProcessesUrl" value="/j_logout.j" />
</bean>
<bean id="authenticationProcessingFilter" class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilter">
<property name="authenticationManager" ref="authenticationManager" />
<!-- 登录失败后的URL -->
<property name="authenticationFailureUrl" value="/login.jsp?msg=%E6%97%A0%E6%95%88%E7%9A%84%E7%94%A8%E6%88%B7%E5%90%8D%E6%88%96%E5%8F%A3%E4%BB%A4" />
<!-- 登录成功后的URL -->
<property name="defaultTargetUrl" value="/user/cmd.jsp" />
<!-- 登录的URL -->
<property name="filterProcessesUrl" value="/j_login.j" />
<property name="rememberMeServices" ref="rememberMeServices" />
</bean>
<bean id="rememberMeFilter" class="org.acegisecurity.ui.rememberme.RememberMeProcessingFilter">
<property name="authenticationManager" ref="authenticationManager" />
<property name="rememberMeServices" ref="rememberMeServices" />
</bean>
<bean id="exceptionFilter" class="org.acegisecurity.ui.ExceptionTranslationFilter">
<!-- 出现AuthenticationException时的登录入口 -->
<property name="authenticationEntryPoint">
   <bean class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilterEntryPoint">
   <property name="loginFormUrl" value="/login.jsp" />
    <property name="forceHttps" value="false" />
   </bean>
</property>
<!-- 出现AccessDeniedException时的Handler -->
<property name="accessDeniedHandler">
   <bean class="org.acegisecurity.ui.AccessDeniedHandlerImpl">
   <property name="errorPage" value="/denied.jsp" />
   </bean>
</property>
</bean>
<bean id="securityInterceptor" class="org.acegisecurity.intercept.web.FilterSecurityInterceptor">
<property name="authenticationManager" ref="authenticationManager" />
<property name="accessDecisionManager" ref="accessDecisionManager" />
<property name="objectDefinitionSource">
   <value>
    CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
    PATTERN_TYPE_APACHE_ANT
    /admin/**=ROLE_ADMIN
    /user/**=ROLE_USER
    /cart/previeworder*=ROLE_USER
   </value>
</property>
</bean>

</beans>

    在上面的配置文件中, 红色部分要特别注意, 其余的内容都差不多了.

<bean id="userDetailsService" class="org.ymcn.security.AcegiUserDeitailsService">
<property name="userDao" ref="userDao" />
<property name="userRoleDao" ref="userRoleDao" />
</bean>

    在整个应用的安全控制中, 我们唯一要编写代码的类就是: org.ymcn.security.AcegiUserDeitailsService

    就连登录和登出的代码也不要了.

三. 修改 web.xml, 增加安全控制过滤链.

<filter>
        <filter-name>acegiFilterChain</filter-name>
        <filter-class>org.acegisecurity.util.FilterToBeanProxy</filter-class>
        <init-param>
            <param-name>targetClass</param-name>
            <param-value>org.acegisecurity.util.FilterChainProxy</param-value>
        </init-param>
    </filter>

<filter-mapping>
        <filter-name>acegiFilterChain</filter-name>
        <url-pattern>*.j</url-pattern>
</filter-mapping>

    注意: 这个过滤器一定要在MVC转发过滤器的前面!!!!

四. 在 applicationContext.xml 中增加 Acegi安全控制拦截器Spring的自动代理功能实现AOP代理

<!-- Acegi安全控制拦截器 -->
<bean id="serviceSecurityInterceptor" class="org.acegisecurity.intercept.method.aopalliance.MethodSecurityInterceptor">
<property name="validateConfigAttributes" value="true" />
<property name="authenticationManager" ref="authenticationManager" />
<property name="accessDecisionManager" ref="accessDecisionManager" />
<property name="objectDefinitionSource">
   <bean class="org.acegisecurity.intercept.method.MethodDefinitionAttributes">
    <property name="attributes">
     <bean class="org.acegisecurity.annotation.SecurityAnnotationAttributes" />
    </property>
   </bean>
</property>
</bean>

<!-- 利用Spring的自动代理功能实现AOP代理 -->
<bean id="autoProxyCreator" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="interceptorNames">
   <list>
    <value>transactionInterceptor</value>
    <value>serviceSecurityInterceptor</value>
   </list>
</property>
<property name="beanNames">
   <list>
    <value>userService</value>
    <value>mailService</value>
   </list>
</property>
</bean>

五. 编写在利用Acegi框架唯一要我们编写的类 AcegiUserDeitailsService.java

package org.ymcn.security;

import java.util.List;

import org.acegisecurity.GrantedAuthority;
import org.acegisecurity.GrantedAuthorityImpl;
import org.acegisecurity.userdetails.UserDetails;
import org.acegisecurity.userdetails.UserDetailsService;
import org.acegisecurity.userdetails.UsernameNotFoundException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.dao.DataAccessException;
import org.ymcn.dao.UserDao;
import org.ymcn.dao.UserRoleDao;
import org.ymcn.model.User;
import org.ymcn.model.UserRole;

public class AcegiUserDeitailsService implements UserDetailsService {
private final Log LOG = LogFactory.getLog(AcegiUserDeitailsService.class);
/* 依赖注入 */
private UserDao userDao;
private UserRoleDao userRoleDao;

public void setUserDao(UserDao userDao) {
   this.userDao = userDao;
}
public void setUserRoleDao(UserRoleDao userRoleDao) {
   this.userRoleDao = userRoleDao;
}

/* 用户所有的权限 */
//private final List<GrantedAuthority> grantedAuthList = new ArrayList<GrantedAuthority>(6);
private GrantedAuthority[] grantedAuthArray;

public UserDetails loadUserByUsername(String userName)
    throws UsernameNotFoundException, DataAccessException {
   if(LOG.isDebugEnabled()) {
    LOG.debug("Loading UserDetails of userName: " + userName);
   }
   /* 取得用户 */
   User user = userDao.getUserByName(userName);
   if(user == null) {
    LOG.warn("UserDetails load failed: No such UserRole with userName: " + userName);
            throw new UsernameNotFoundException("User name is not found.");
   }
   /* 取得所有用户权限 */
   List<UserRole> userRoleList = userRoleDao.getUserRoleByUserName(userName);
   if(userRoleList == null || userRoleList.size() == 0) {
    LOG.warn("UserRole load failed: No such UserRole with userName: " + userName);
            throw new UsernameNotFoundException("UserRole is not found.");
   }
   /* 取得用户的所有角色 */
   int size = userRoleList.size();
   grantedAuthArray = new GrantedAuthority[size];
   int j = 0;
   for(int i = 0; i < size; i++) {
    UserRole userRole = userRoleList.get(i);
    if(userRole != null) {
     this.grantedAuthArray[j++] = new GrantedAuthorityImpl(userRole.getRoleName().toUpperCase());
    }
   }
   LOG.info("UserName: " + userName + " loaded successfully.");
        return new org.acegisecurity.userdetails.User(userName, user.getUserPass(),
           true, true, true, true, this.grantedAuthArray);
}
}

六. 在业务逻辑代码中利用Java 5注释实现安全控制

@Secured({"ROLE_USER"})
void sendSimpleMail(Long userId);

@Secured({"ROLE_ADMIN"})
void sendAttachmentMail() throws Exception;

    其实就是在需要安全控制的方法前加上: @Secured({"角色名"}), 非常的简单

七. 整个工作完成

    Acegi框架完全是一种可插拔式的, 完全可以在原有的系统中加个一个配置文件, 和在每个方法前加上: @Secured({"角色名"}) 就可完成.

     上面的 AcegiUserDeitailsService.java 中的有 UserDao, UserRoleDao, 我想一看就知道它们是干什么的了, 这完全取决于个人的实现, 与Acegi无关, 它仅仅只要返回一个 return new org.acegisecurity.userdetails.User(userName, user.getUserPass(),
          true, true, true, true, this.grantedAuthArray) 就可以了.

    由于时间仓促, 如有遗漏, 疑问, 批评, 指教, 请: obullxl@163.com

    祝大家使用JAVA愉快!!!

这篇关于Acegi + Spring + Hibernate + Struts 2搭建基于角色的权限控制系统的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/610038

相关文章

Java实现将HTML文件与字符串转换为图片

《Java实现将HTML文件与字符串转换为图片》在Java开发中,我们经常会遇到将HTML内容转换为图片的需求,本文小编就来和大家详细讲讲如何使用FreeSpire.DocforJava库来实现这一功... 目录前言核心实现:html 转图片完整代码场景 1:转换本地 HTML 文件为图片场景 2:转换 H

Java使用jar命令配置服务器端口的完整指南

《Java使用jar命令配置服务器端口的完整指南》本文将详细介绍如何使用java-jar命令启动应用,并重点讲解如何配置服务器端口,同时提供一个实用的Web工具来简化这一过程,希望对大家有所帮助... 目录1. Java Jar文件简介1.1 什么是Jar文件1.2 创建可执行Jar文件2. 使用java

SpringBoot实现不同接口指定上传文件大小的具体步骤

《SpringBoot实现不同接口指定上传文件大小的具体步骤》:本文主要介绍在SpringBoot中通过自定义注解、AOP拦截和配置文件实现不同接口上传文件大小限制的方法,强调需设置全局阈值远大于... 目录一  springboot实现不同接口指定文件大小1.1 思路说明1.2 工程启动说明二 具体实施2

Java实现在Word文档中添加文本水印和图片水印的操作指南

《Java实现在Word文档中添加文本水印和图片水印的操作指南》在当今数字时代,文档的自动化处理与安全防护变得尤为重要,无论是为了保护版权、推广品牌,还是为了在文档中加入特定的标识,为Word文档添加... 目录引言Spire.Doc for Java:高效Word文档处理的利器代码实战:使用Java为Wo

SpringBoot日志级别与日志分组详解

《SpringBoot日志级别与日志分组详解》文章介绍了日志级别(ALL至OFF)及其作用,说明SpringBoot默认日志级别为INFO,可通过application.properties调整全局或... 目录日志级别1、级别内容2、调整日志级别调整默认日志级别调整指定类的日志级别项目开发过程中,利用日志

Java中的抽象类与abstract 关键字使用详解

《Java中的抽象类与abstract关键字使用详解》:本文主要介绍Java中的抽象类与abstract关键字使用详解,本文通过实例代码给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧... 目录一、抽象类的概念二、使用 abstract2.1 修饰类 => 抽象类2.2 修饰方法 => 抽象方法,没有

SpringBoot 多环境开发实战(从配置、管理与控制)

《SpringBoot多环境开发实战(从配置、管理与控制)》本文详解SpringBoot多环境配置,涵盖单文件YAML、多文件模式、MavenProfile分组及激活策略,通过优先级控制灵活切换环境... 目录一、多环境开发基础(单文件 YAML 版)(一)配置原理与优势(二)实操示例二、多环境开发多文件版

Spring 中的切面与事务结合使用完整示例

《Spring中的切面与事务结合使用完整示例》本文给大家介绍Spring中的切面与事务结合使用完整示例,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考... 目录 一、前置知识:Spring AOP 与 事务的关系 事务本质上就是一个“切面”二、核心组件三、完

使用docker搭建嵌入式Linux开发环境

《使用docker搭建嵌入式Linux开发环境》本文主要介绍了使用docker搭建嵌入式Linux开发环境,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面... 目录1、前言2、安装docker3、编写容器管理脚本4、创建容器1、前言在日常开发全志、rk等不同

Java实现远程执行Shell指令

《Java实现远程执行Shell指令》文章介绍使用JSch在SpringBoot项目中实现远程Shell操作,涵盖环境配置、依赖引入及工具类编写,详解分号和双与号执行多指令的区别... 目录软硬件环境说明编写执行Shell指令的工具类总结jsch(Java Secure Channel)是SSH2的一个纯J