用户权限管理spring security

来源:互联网 发布:dede上传网站源码 编辑:程序博客网 时间:2024/05/01 05:59

分享一下自己的经验,还望大神能够指点。目前此项目只是基础的,后面会加入用户,角色,资源的管理界面。

暂时导入的jar包,等界面好后,会改成maven项目。以后会把此项目放到github上。

暂时用jquery easy-ui,以后界面会改成bootstrap,如果是只想要源码的可以直接看结尾。

由于时间关系只是简单说了下,若有不懂的,还望见谅。

2015-12-17:已经整合jquery easyui实现了用户,角色,模块的管理

   

项目结构图:


最主要的5张表


Sys_function是资源表也就是访问时的URL

Sys_function_role是资源和角色关系表

Sys_role是角色表

Sys_user是用户表

Sys_user_role是用户角色表

Sys_function


Sys_function_role


Sys_role


Sys_user


Sys_user_role


简单说下Spring mvc mybatis的搭建:

首先是web.xml的配置

主要配置spring的配置文件位置,字符过滤器,日志记录,监听器,spring security的配置,还有spring mvc的核心配置。

<?xmlversion="1.0"encoding="UTF-8"?>

<web-appversion="2.5"

   xmlns="http://java.sun.com/xml/ns/javaee"

   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

   xsi:schemaLocation="http://java.sun.com/xml/ns/javaee

   http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">

  <display-name>BaseJava</display-name>  

 <welcome-file-list>

      <welcome-file>index.html</welcome-file>

      <welcome-file>index.htm</welcome-file>

      <welcome-file>index.jsp</welcome-file>

      <welcome-file>default.html</welcome-file>

      <welcome-file>default.htm</welcome-file>

      <welcome-file>default.jsp</welcome-file>

   </welcome-file-list>

   <!-- 读取spring配置文件 -->

   <context-param>

      <param-name>contextConfigLocation</param-name>

      <param-value>classpath:config/spring.xml

        </param-value>

   </context-param>

   <!-- Spring字符集过滤器 -->

   <filter>

      <filter-name>SpringEncodingFilter</filter-name>

   <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>

      <init-param>

          <param-name>encoding</param-name>

          <param-value>UTF-8</param-value>

      </init-param>

      <init-param>

         <param-name>forceEncoding</param-name>

          <param-value>true</param-value>

      </init-param>

   </filter>

   <filter-mapping>

      <filter-name>SpringEncodingFilter</filter-name>

      <url-pattern>/*</url-pattern>

   </filter-mapping>

   <!-- 日志记录 -->

   <context-param>

      <!-- 日志配置文件路径 -->

      <param-name>log4jConfigLocation</param-name>

   <param-value>classpath:config/properties/log4j.properties</param-value>

   </context-param>

   <context-param>

      <!-- 日志页面的刷新间隔 -->

      <param-name>log4jRefreshInterval</param-name>

      <param-value>6000</param-value>

   </context-param>

   <listener>

   <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>

   </listener>

   <listener>

   <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>

   </listener>

   <!--多个用户不能使用同一个账号同时登陆系统。为了实现这个功能,我们首先要在 web.xml

             文件中添加一个监听器,这个监听器会在 session创建和销毁的时候通知 Spring Security  -->

   <listener>

      <listener-class>

     org.springframework.security.web.session.HttpSessionEventPublisher

      </listener-class>

   </listener>

     <!-- 权限  SpringSecurity3.1的权限过滤-->

  <filter>

        <filter-name>springSecurityFilterChain</filter-name>

        <filter-class>

           org.springframework.web.filter.DelegatingFilterProxy

        </filter-class>

   </filter>

    <filter-mapping>

        <filter-name>springSecurityFilterChain</filter-name>

        <url-pattern>/*</url-pattern>

    </filter-mapping>

   <!-- springMVC核心配置 -->

   <servlet>

      <servlet-name>spring</servlet-name>

   <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

      <init-param>

          <param-name>contextConfigLocation</param-name>

         <param-value>classpath:config/spring/spring-mvc.xml</param-value>

      </init-param>

      <load-on-startup>2</load-on-startup>

   </servlet>

   <servlet-mapping>

      <servlet-name>spring</servlet-name>

      <url-pattern>*.do</url-pattern>

   </servlet-mapping>

 

   <session-config>

      <session-timeout>30</session-timeout>

   </session-config>

</web-app>

然后是spring的配置spring.xml文件

Jdbc.properties文件位置配置,文件扫描service注入

导入spring-mybatisspring-security文件

<?xmlversion="1.0"encoding="UTF-8"?>

<beansxmlns="http://www.springframework.org/schema/beans"

   xmlns:context="http://www.springframework.org/schema/context"

   xmlns:aop="http://www.springframework.org/schema/aop"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-3.0.xsd

      http://www.springframework.org/schema/context

     http://www.springframework.org/schema/context/spring-context.xsd

      http://www.springframework.org/schema/aop

     http://www.springframework.org/schema/aop/spring-aop.xsd">

 

   <context:property-placeholder

      location="classpath:config/properties/jdbc.properties"/>

   <context:annotation-config/>

   <!-- 扫描文件(自动将servicec层注入) -->

   <context:component-scanbase-package="com.base.service"/>

   <!-- <context:component-scanbase-package="com.limx.service" /> <context:component-scan

      base-package="com.ly.service"/> -->

 

   <aop:aspectj-autoproxy/>

   <importresource="./spring/spring-mybatis.xml"/>

   <importresource="./spring/spring-security.xml"/>

</beans>

Spring-mvc.xml文件配置

Controller层的注入、jsp视图配置、文件上传

<?xmlversion="1.0"encoding="UTF-8"?>

<beansxmlns="http://www.springframework.org/schema/beans"

   xmlns:p="http://www.springframework.org/schema/p"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

   xmlns:context="http://www.springframework.org/schema/context"

   xmlns:mvc="http://www.springframework.org/schema/mvc"

   xsi:schemaLocation="

    http://www.springframework.org/schema/beans

   http://www.springframework.org/schema/beans/spring-beans-3.2.xsd

   http://www.springframework.org/schema/context

    http://www.springframework.org/schema/context/spring-context-3.2.xsd

    http://www.springframework.org/schema/mvc

   http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd">

 

   <!-- 扫描controllercontroller层注入) -->

   <context:component-scanbase-package="com.base.controller"/>

   <!--2: 在此处添加controller -->

   <!-- 开启事务注解驱动 -->

   <context:annotation-config/>

   <!-- 开启事务注解驱动 -->

   <mvc:annotation-driven/>

 

   <!-- 避免IEajax请求时,返回json出现下载 -->

   <beanid="jacksonMessageConverter"

   class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">

      <propertyname="supportedMediaTypes">

          <list>

             <value>text/html;charset=UTF-8</value>

          </list>

      </property>

   </bean>

 

   <!-- jsp视图 -->

   <beanclass="org.springframework.web.servlet.view.InternalResourceViewResolver">

      <propertyname="viewClass"value="org.springframework.web.servlet.view.JstlView"/>

      <!-- 访问时添加的默认前缀 -->

      <propertyname="prefix"value="/"/>

      <!-- 访问时添加的默认后缀 -->

      <propertyname="suffix"value=".jsp"/>

   </bean>

   <!-- 上传附件 -->

   <beanid="multipartResolver"

   class="org.springframework.web.multipart.commons.CommonsMultipartResolver">

      <propertyname="maxUploadSize"value="104857600"/>

      <propertyname="maxInMemorySize"value="4096"/>

   </bean>

 

</beans>

Spring-mybatis配置

数据源datasourcemapper文件配置(采用mybatis物理分页、mybatis-config文件位置)Dao层注入、事物处理,切面

<?xmlversion="1.0"encoding="UTF-8"?>

<beansxmlns="http://www.springframework.org/schema/beans"

   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:p="http://www.springframework.org/schema/p"

   xmlns:context="http://www.springframework.org/schema/context"

   xmlns:aop="http://www.springframework.org/schema/aop"xmlns:tx="http://www.springframework.org/schema/tx"

   xmlns:util="http://www.springframework.org/schema/util"

   xsi:schemaLocation="http://www.springframework.org/schema/beans

   http://www.springframework.org/schema/beans/spring-beans-3.2.xsd

   http://www.springframework.org/schema/context

    http://www.springframework.org/schema/context/spring-context-3.2.xsd

    http://www.springframework.org/schema/tx

   http://www.springframework.org/schema/tx/spring-tx-3.2.xsd

    http://www.springframework.org/schema/aop

    http://www.springframework.org/schema/aop/spring-aop-3.2.xsd

    http://www.springframework.org/schema/util

   http://www.springframework.org/schema/util/spring-util-3.2.xsd">

 

   <beanid="dataSource"class="com.alibaba.druid.pool.DruidDataSource"

      init-method="init"destroy-method="close">

      <propertyname="driverClassName">

          <value>${jdbc_driverClassName}</value>

      </property>

      <propertyname="url">

          <value>${jdbc_url}</value>

      </property>

      <propertyname="username">

          <value>${jdbc_username}</value>

      </property>

      <propertyname="password">

          <value>${jdbc_password}</value>

      </property>

      <!-- 连接池最大使用连接数 -->

      <propertyname="maxActive">

          <value>20</value>

      </property>

      <!-- 初始化连接大小 -->

      <propertyname="initialSize">

          <value>1</value>

      </property>

      <!-- 获取连接最大等待时间 -->

      <propertyname="maxWait">

          <value>60000</value>

      </property>

      <!-- 连接池最大空闲 -->

      <propertyname="maxIdle">

          <value>20</value>

      </property>

      <!-- 连接池最小空闲 -->

      <propertyname="minIdle">

          <value>3</value>

      </property>

      <!-- 自动清除无用连接 -->

      <propertyname="removeAbandoned">

          <value>true</value>

      </property>

      <!-- 清除无用连接的等待时间 -->

      <propertyname="removeAbandonedTimeout">

          <value>180</value>

      </property>

      <!-- 连接属性 -->

      <propertyname="connectionProperties">

          <value>clientEncoding=UTF-8</value>

      </property>

   </bean>

 

   <!-- mybatis文件配置,扫描所有mapper文件 -->

   <beanid="sqlSessionFactory"class="org.mybatis.spring.SqlSessionFactoryBean"

      p:dataSource-ref="dataSource"p:configLocation="classpath:config/spring/mybatis-config.xml"

      p:mapperLocations="classpath:config/mapper/**/*.xml">

      <propertyname="plugins">

          <array>

             <beanclass="com.github.pagehelper.PageHelper">

                <property name="properties">

                   <value>

                      dialect=hsqldb

                      reasonable=true

                      params=count=countSql;pageSizeZero=zero

                   </value>

                </property>

             </bean>

          </array>

      </property>

   </bean>

   <!-- configLocationmybatis属性mapperLocations为所有mapper -->

 

   <!-- springmybatis整合配置,扫描所有dao -->

   <beanclass="org.mybatis.spring.mapper.MapperScannerConfigurer"

      p:basePackage="com.base.dao"

      p:sqlSessionFactoryBeanName="sqlSessionFactory"/>

  

   <!-- 对数据源进行事务管理 -->

   <beanid="transactionManager"

   class="org.springframework.jdbc.datasource.DataSourceTransactionManager"

      p:dataSource-ref="dataSource"/>

   <!-- 配置切面 -->

   <aop:config>

      <aop:pointcut

          expression="execution(* com.base.service.*.*(..))"

          id="myTx"/>

      <aop:advisoradvice-ref="txAdvice"pointcut-ref="myTx"/>

   </aop:config>

   <!-- 配制具体方法及事务参数异常回滚 -->

   <tx:adviceid="txAdvice"transaction-manager="transactionManager">

      <tx:attributes>

          <tx:methodname="insert*"propagation="REQUIRED"read-only="false"

             rollback-for="Exception" />

          <tx:methodname="update*"propagation="REQUIRED"read-only="false"

             rollback-for="Exception" />

          <tx:methodname="delete*"propagation="REQUIRED"read-only="false"

             rollback-for="Exception" />

          <tx:methodname="find*"propagation="SUPPORTS"/>

         <tx:methodname="get*"propagation="SUPPORTS"/>

          <tx:methodname="select*"propagation="SUPPORTS"/>

      </tx:attributes>

   </tx:advice>

 

 

 

</beans>

Spring-security

如果对spring-security基础不懂可以先看下

http://download.csdn.net/detail/u012367513/7826801

如果上面的基础会了可以看这个

http://blog.csdn.net/u012367513/article/details/38866465

实现AbstractSecurityInterceptor

authenticationManager、处理验证的(UserDetailsService)

accessDecisionManager、授权器----通过登录用户的权限信息、资源、获取资源所需的权限来根据不同的授权策略来判断用户是否有权限访问资源

securityMetadataSource、加载资源与权限的全部对应关系的,并提供一个通过资源获取所有权限的方法

spring-security.xml

<?xmlversion="1.0"encoding="UTF-8"?>

<b:beansxmlns="http://www.springframework.org/schema/security"

   xmlns:b="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

   xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-3.0.xsd 

                       http://www.springframework.org/schema/securityhttp://www.springframework.org/schema/security/spring-security-3.1.xsd">

 

   <!--登录页面不过滤 -->

   <httppattern="/login.jsp"security="none"/>

   <httppattern="/accessDenied.jsp"security="none"/>

   <httpauto-config="true"access-denied-page="/accessDenied.jsp">

      <form-loginlogin-page="/login.jsp"/>

      <session-management>

          <concurrency-controlmax-sessions="1"

             error-if-maximum-exceeded="false"/>

      </session-management>

      <!--增加一个filter,这点与Acegi是不一样的,不能修改默认的filter了,这个filter位于FILTER_SECURITY_INTERCEPTOR之前 -->

      <custom-filterref="myFilter"before="FILTER_SECURITY_INTERCEPTOR"/>

      <!--登录处理 -->

      <!-- <custom-filterref="myLoginFilter"position="FORM_LOGIN_FILTER" /> -->

   </http>

   <!--一个自定义的filter,必须包含authenticationManager,accessDecisionManager,securityMetadataSource三个属性,

      我们的所有控制将在这三个类中实现,解释详见具体配置 -->

   <b:beanid="myFilter"class="com.base.security.MySecurityFilter">

      <!-- 处理验证的,这里需要特别说明的是:这个类不单只这个拦截器用到,还有验证拦截器AuthenticationProcessingFilter也用到

          了,而且实际上的登陆验证也是AuthenticationProcessingFilter拦截器调用authenticationManager来处理的,

          我们这个拦截器只是为了拿到验证用户信息而已(这里不太清楚,因为authenticationManager笔者设了断点,用户登陆后再也没调用这个类了,而且调用这个类时不是笔者自己写的那个拦截器调用的,

          看了spring技术内幕这本书才知道是AuthenticationProcessingFilter拦截器调用的)。 -->

      <b:propertyname="authenticationManager"ref="authenticationManager"/>

      <!--这个也称为授权器,通过登录用户的权限信息、资源、获取资源所需的权限来根据不同的授权策略来判断用户是否有权限访问资源。 -->

      <b:propertyname="accessDecisionManager"ref="myAccessDecisionManagerBean"/>

      <!--这个用来加载资源与权限的全部对应关系的,并提供一个通过资源获取所有权限的方法。 -->

      <b:propertyname="securityMetadataSource"ref="securityMetadataSource"/>

   </b:bean>

   <!--验证配置,认证管理器,实现用户认证的入口,主要实现UserDetailsService接口即可 -->

   <authentication-manageralias="authenticationManager">

      <authentication-provideruser-service-ref="myUserDetailService">

          <!--如果用户的密码采用加密的话 <password-encoder hash="md5"/> -->

      </authentication-provider>

   </authentication-manager>

   <!--在这个类中,你就可以从数据库中读入用户的密码,角色信息,是否锁定,账号是否过期等 -->

   <b:beanid="myUserDetailService"class="com.base.security.MyUserDetailServiceImpl"/>

   <!--访问决策器,决定某个用户具有的角色,是否有足够的权限去访问某个资源 -->

   <b:beanid="myAccessDecisionManagerBean"class="com.base.security.MyAccessDecisionManager">

   </b:bean>

   <!--资源源数据定义,将所有的资源和权限对应关系建立起来,即定义某一资源可以被哪些角色访问 -->

   <b:beanid="securityMetadataSource"class="com.base.security.MySecurityMetadataSource"/>

   <b:beanid="messageSource"

   class="org.springframework.context.support.ReloadableResourceBundleMessageSource">

      <!-- <property name="basename"value="classpath:org/springframework/security/messages_zh_CN"></property>-->

      <b:propertyname="basename"

         value="classpath:config/properties/message_zh_CN"></b:property>

   </b:bean>

   <!--登录处理  -->

<!-- <b:bean id="myLoginFilter"init-method="init"

      class="com.base.security.MyAuthenticationFilter">

      <b:propertyname="authenticationManager"ref="authenticationManager"/>

      <b:propertyname="filterProcessesUrl"value="/background/j_security_check" />

      验证成功后要跳转的URL

      <b:property name="successUrl"value="/background/index.html" />

      验证失败后要跳转的URL

      <b:property name="errorUrl"value="/background/login.html" />

   </b:bean> -->

</b:beans> 

MyAccessDecisionManager

package com.base.security;

 

import java.util.Collection;

import java.util.Iterator;

 

importorg.springframework.security.access.AccessDecisionManager;

import org.springframework.security.access.AccessDeniedException;

importorg.springframework.security.access.ConfigAttribute;

importorg.springframework.security.access.SecurityConfig;

importorg.springframework.security.authentication.InsufficientAuthenticationException;

importorg.springframework.security.core.Authentication;

importorg.springframework.security.core.GrantedAuthority;

 

/**

 * 这个也称为授权器,通过登录用户的权限信息、资源、获取资源所需的权限来根据不同的授权策略来判断用户是否有权限访问资源。

 * <!-- 用户是否拥有所请求资源的权限 -->

 * @author limingxing

 * @2015-12-2 version 1.0v

 */

public classMyAccessDecisionManager implements AccessDecisionManager {

   /**

    * 检查用户是否够权限访问资源

    * 参数authentication是从spring的全局缓存SecurityContextHolder中拿到的,里面是用户的权限信息

    * 参数object是url

    * 参数configAttributes所需的权限

    */

   publicvoid decide(Authentication authentication, Object object,

          Collection<ConfigAttribute>configAttributes)

          throwsAccessDeniedException, InsufficientAuthenticationException {

       if(configAttributes == null){  

               return;        

           }   

            

           Iterator<ConfigAttribute>ite=configAttributes.iterator(); 

           while(ite.hasNext()){ 

               ConfigAttribute ca=ite.next();   

               StringneedRole=((SecurityConfig)ca).getAttribute(); 

               for(GrantedAuthority ga :authentication.getAuthorities()){  

                  if(needRole.equals(ga.getAuthority())){   

                        

                       return;               

           }             

       }       

   }  

           //注意:执行这里,后台是会抛异常的,但是界面会跳转到所配的access-denied-page页面 

      thrownew AccessDeniedException(" 没有权限访问! ");

 

   }

 

   publicboolean supports(ConfigAttribute arg0) {

      //TODO Auto-generated method stub

      returntrue;

   }

 

   publicboolean supports(Class<?> arg0) {

      //TODO Auto-generated method stub

      returntrue;

   }

 

}

MySecurityFilter

package com.base.security;

 

import java.io.IOException;

 

import javax.servlet.Filter;

import javax.servlet.FilterChain;

importjavax.servlet.FilterConfig;

importjavax.servlet.ServletException;

import javax.servlet.ServletRequest;

importjavax.servlet.ServletResponse;

 

importorg.springframework.beans.factory.annotation.Autowired;

importorg.springframework.security.access.SecurityMetadataSource;

import org.springframework.security.access.intercept.AbstractSecurityInterceptor;

importorg.springframework.security.access.intercept.InterceptorStatusToken;

importorg.springframework.security.authentication.AuthenticationManager;

import org.springframework.security.web.FilterInvocation;

importorg.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;

 

/**

 * 首先,登陆后,每次访问资源都会被这个拦截器拦截,会执行doFilter这个方法,这个方法调用了invoke方法,其中fi断点显示是一个url(

 * 可能重写了toString方法吧,但是里面还有一些方法的),最重要的是beforeInvocation这个方法,

 * 它首先会调用MyInvocationSecurityMetadataSource类的getAttributes方法获取被拦截url所需的权限

 * ,在调用MyAccessDecisionManager类decide方法判断用户是否够权限。弄完这一切就会执行下一个拦截器。

 * 核心的InterceptorStatusToken token =

 * super.beforeInvocation(fi);会调用我们定义的accessDecisionManager:decide(Object

 * object)和securityMetadataSource:getAttributes(Object object)方法。

 *

 * 自己实现的过滤用户请求类,也可以直接使用FilterSecurityInterceptor

 * AbstractSecurityInterceptor有三个派生类:

 * FilterSecurityInterceptor,负责处理FilterInvocation,实现对URL资源的拦截。

 * MethodSecurityInterceptor,负责处理MethodInvocation,实现对方法调用的拦截。

 * AspectJSecurityInterceptor,负责处理JoinPoint,主要是用于对切面方法(AOP)调用的拦截。

 * 还可以直接使用注解对Action方法进行拦截,例如在方法上加:

 *@PreAuthorize("hasRole('ROLE_SUPER')")

 *

 * @author limingxing

 * @2015-12-3

 * version 1.0v

 */

public class MySecurityFilterextends AbstractSecurityInterceptor implements Filter{

   //配置文件注入 

   @Autowired

    privateFilterInvocationSecurityMetadataSource securityMetadataSource;

   publicvoid destroy() {

      //TODO Auto-generated method stub

     

   }

   publicvoid doFilter(ServletRequest request, ServletResponse response,

          FilterChainchain) throws IOException, ServletException {

       FilterInvocation fi = newFilterInvocation(request, response, chain);  

           invoke(fi);   

    }

    public void invoke(FilterInvocation fi) throwsIOException, ServletException { 

           //fi里面有一个被拦截的url 

           //里面调用MyInvocationSecurityMetadataSource的getAttributes(Objectobject)这个方法获取fi对应的所有权限 

           //再调用MyAccessDecisionManager的decide方法来校验用户的权限是否足够 

           InterceptorStatusToken token =super.beforeInvocation(fi); 

           try { 

               //执行下一个拦截器 

              fi.getChain().doFilter(fi.getRequest(), fi.getResponse());    

               } finally {  

                   super.afterInvocation(token,null);   

               }    

           }

   publicvoid init(FilterConfig arg0) throws ServletException {

   }

   @Override

   publicClass<?> getSecureObjectClass() {

      //TODO Auto-generated method stub

      returnFilterInvocation.class;

   }

 

   @Override

   publicSecurityMetadataSource obtainSecurityMetadataSource() {

      //TODO Auto-generated method stub

      returnthis.securityMetadataSource;

   }

   publicFilterInvocationSecurityMetadataSource getSecurityMetadataSource() {

      returnsecurityMetadataSource;

   }

   publicvoid setSecurityMetadataSource(

          FilterInvocationSecurityMetadataSourcesecurityMetadataSource) {

      this.securityMetadataSource= securityMetadataSource;

   }

 

  

 

  

 

}

MySecurityMetadataSource


packagecom.base.security;

 

import java.util.ArrayList;

import java.util.Collection;

import java.util.HashMap;

import java.util.Iterator;

import java.util.List;

import java.util.Map;

 

importjavax.annotation.PostConstruct;

 

importorg.springframework.beans.factory.annotation.Autowired;

import org.springframework.security.access.ConfigAttribute;

importorg.springframework.security.access.SecurityConfig;

importorg.springframework.security.web.FilterInvocation;

importorg.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;

 

import com.base.dao.FunctionDAO;

import com.base.dao.RoleDAO;

import com.base.model.Function;

import com.base.model.Role;

importcom.base.util.UrlPathMatcher;

 

/**

 *这个用来加载资源与权限的全部对应关系的,并提供一个通过资源获取所有权限的方法。

 *首先,这里也是模拟了从数据库中获取信息。

 *其中loadResourceDefine方法不是必须的

 * ,这个只是加载所有的资源与权限的对应关系并缓存起来,避免每次获取权限都访问数据库(提高性能),然后getAttributes根据参数

 * (被拦截url)返回权限集合。

 * 这种缓存的实现其实有一个缺点,因为loadResourceDefine方法是放在构造器上调用的,而这个类的实例化只在web服务器启动时调用一次

 * ,那就是说loadResourceDefine方法只会调用一次

 * ,如果资源和权限的对应关系在启动后发生了改变,那么缓存起来的就是脏数据,现在这里使用的就是缓存数据

 * ,那就会授权错误了。但如果资源和权限对应关系是不会改变的,这种方法性能会好很多。

 * 在getAttributes方法里面调用dao(这个是加载完,后来才会调用的,所以可以使用dao)

 *  ,通过被拦截url获取数据库中的所有权限,封装成Collection<ConfigAttribute>返回就行了。(灵活、简单)

 * @author limingxing

 * @2015-12-3

 * version 1.0v

 */

public classMySecurityMetadataSource implements

      FilterInvocationSecurityMetadataSource{

   privatestatic Map<String, Collection<ConfigAttribute>> resourceMap = null;

   @Autowired

   privateFunctionDAO functionDAO;

   @Autowired

   privateRoleDAO roleDAO;

   privateUrlPathMatcher urlMatcher = new UrlPathMatcher();

 

   /**

    * @PostConstruct是Java EE 5引入的注解, Spring允许开发者在受管Bean中使用它。当DI容器实例化当前受管Bean时,

    * @PostConstruct注解的方法会被自动触发,从而完成一些初始化工作,

    *

    *加载所有资源与权限的关系

    */

   @PostConstruct

   privatevoid loadResourceDefine() {

          resourceMap= new HashMap<String, Collection<ConfigAttribute>>();

          List<Role>roles = roleDAO.findAllEffectiveRoles();

          for(Role role : roles) {

             List<Function>functions = this.functionDAO

                   .findAllEffectiveFunctionsbyRoleId(role.getId());

             for(Function function : functions) {

                Collection<ConfigAttribute>configAttributes = new ArrayList<ConfigAttribute>();

                ConfigAttributeconfigAttribute = new SecurityConfig(

                      role.getRoleName());

                configAttributes.add(configAttribute);

                Stringurl=function.getAction();

                 if (resourceMap.containsKey(url)) {

 

                         Collection<ConfigAttribute> value = resourceMap.get(url); //取出这个url的权限集合

                          value.add(configAttribute);

                          resourceMap.put(url,value);

                      } else {

                         Collection<ConfigAttribute> atts = newArrayList<ConfigAttribute>();

                          atts.add(configAttribute);

                          resourceMap.put(url, atts);

                      }

             }

          }

   }

 

   publicCollection<ConfigAttribute> getAllConfigAttributes() {

      //TODO Auto-generated method stub

      returnnull;

   }

 

   /**

    * 参数是要访问的url,返回这个url对应的所有权限(或角色)

    */

   publicCollection<ConfigAttribute> getAttributes(Object object)

          throwsIllegalArgumentException {

      loadResourceDefine();

      //object getRequestUrl 是获取用户请求的url地址

      Stringurl = ((FilterInvocation) object).getRequestUrl();

 

      //resourceMap保存了loadResourceDefine方法加载进来的数据

      Iterator<String>ite = resourceMap.keySet().iterator();

 

      while(ite.hasNext()) {

 

          //取出resourceMap中读取数据库的url地址

          StringresURL = ite.next();

 

          //如果两个

          //url地址相同,那么将返回resourceMap中对应的权限集合,然后跳转到MyAccessDecisionManager类里的decide方法,再判断权限

          if(urlMatcher.pathMatchesUrl(url, resURL)) {

             returnresourceMap.get(resURL); // 返回对应的url地址的权限

             //,resourceMap是一个主键为地址,值为权限的集合对象

          }

      }

 

      //如果上面的两个url地址没有匹配,返回return

      //null,不再调用MyAccessDecisionManager类里的decide方法进行权限验证,代表允许访问页面

      returnnull;

   }

 

   publicboolean supports(Class<?> arg0) {

      //TODO Auto-generated method stub

      returntrue;

   }

 

}

MyUserDetailServiceImpl

      package com.base.security;

 

importjava.util.Collection;

importjava.util.HashSet;

importjava.util.List;

importjava.util.Set;

 

importorg.springframework.beans.factory.annotation.Autowired;

importorg.springframework.security.core.GrantedAuthority;

importorg.springframework.security.core.authority.SimpleGrantedAuthority;

importorg.springframework.security.core.userdetails.UserDetails;

importorg.springframework.security.core.userdetails.UserDetailsService;

importorg.springframework.security.core.userdetails.UsernameNotFoundException;

importcom.base.dao.RoleDAO;

importcom.base.dao.UserDao;

importcom.base.model.Role;

importcom.base.model.User;

 

 

/**

 *通过MyUserDetailServiceImpl拿到用户信息后,authenticationManager对比用户的密码(即验证用户)

 *

 * User userdetail该类实现 UserDetails 接口,该类在验证成功后会被保存在当前回话的principal对象中

 * 获得对象的方式:

 * WebUserDetails webUserDetails =(WebUserDetails)SecurityContextHolder.getContext().getAuthentication().getPrincipal();

 *

 * 或在JSP中:

 * <sec:authenticationproperty="principal.username"/>

 *

 * 如果需要包括用户的其他属性,可以实现 UserDetails 接口中增加相应属性即可

 * 权限验证类

 * @author lanyuan

 * 2013-11-19

 * @author limingxing

 * @2015-12-2

 * version 1.0v

 */

public classMyUserDetailServiceImpl implements UserDetailsService{

      @Autowired

      private UserDao userDao;

      @Autowired

      private RoleDAO roleDAO;

     

      /**

       *  登陆验证时,通过username获取用户的所有权限信息,

       *   并返回User放到spring的全局缓存SecurityContextHolder中,以供授权器使用

       */

      public UserDetailsloadUserByUsername(String loginName)

                  throwsUsernameNotFoundException {

            User user=null;

            List<User>list=userDao.findUserByLoginName(loginName);

            if(list!=null&&list.size()!=0)

                  user=list.get(0);

            Collection<GrantedAuthority>grantedAuths = obtionGrantedAuthorities(user);

            org.springframework.security.core.userdetails.Useruserdetail = new org.springframework.security.core.userdetails.User(

                       user.getLoginName(),user.getPassword(), true, true, true, true,

                       grantedAuths);

            return userdetail;

      }

      /**

       *取得用户的权限

       *@param user

       *@return

       */

      private Set<GrantedAuthority>obtionGrantedAuthorities(User user) {

            List<Role>roles=roleDAO.findRolesByLoginName(user.getLoginName());

                  Set<GrantedAuthority>authSet = new HashSet<GrantedAuthority>();

                  for (Role res : roles) {

                       // 用户可以访问的资源名称(或者说用户所拥有的权限) 注意:必须"ROLE_"开头

                       authSet.add(newSimpleGrantedAuthority(res.getRoleName()));

                  }

                  return authSet;

            }

}

项目源码以及数据库下载地址

原地址:

http://download.csdn.net/detail/u010880345/9328881

新地址:

http://download.csdn.net/detail/u010880345/9368465

GitHub地址:

https://github.com/lilongsen/MyDream/

GitHub Maven版本:

https://github.com/LimxBoys/BaseJava

Maven版本会一直更新的。

登陆时的用户名:limingxing密码:123456

新的地址已更新.已经整合jquery easyui实现了用户,角色,模块的管理。目前在学习spring security oauth2.0,本来是在学习分布式缓存memcached的,这个以后再搞了。希望如果有熟悉spring security oauth2.0的可以交流下。

如果有进一步想交流的可以给我发邮箱

邮箱地址limingxing_aqgy@sina.com



3 0
原创粉丝点击