zoukankan      html  css  js  c++  java
  • Shiro集成Spring

    本篇博客主要讲述的是两者的集成。不涉及到各自的详细细节和功能。

    因为官方给出的文档不够具体,对新手而言通过官方文档还不可以非常快的搭建出SpringShiro的webproject。本博客将通过实际的案例提供具体的教程。

    案例分析:

    项目名称:假期系统

    组织机构:部门 > 小组

    角色:Admin, SeniorManager,TeamLeader,Developer

    资源:假期Leave

    权限:申请Apply,审核Review, 复核ReReview。查看View

    角色级别:Admin > Senior Manager >Team Leader > Developer

    角色权限:

     

    Admin

    Senior Manager

    Team Leader

    Developer

    Apply

    Y

    Y

    Y

    Y

    Review

    Y

    Y

    Y

    N

    ReReview

    Y

    Y

    N

    N

    View

    Y

    Y

    Y

    Y

    特殊需求:

    1. 角色能够绑定到不同的组织机构级别,比方SeniorManager 是基于部门的,TeamLeader则是基于小组的

    2. 角色的级别关系仅仅能在同样的部门中,不同部门之间的角色没有关联

    3. 审核仅仅能是自己上级角色审核,复核必须是审核者的上级角色复核,即Developer提出的请假申请仅仅能TeamLeader来审核,而且由SeniorManager复核。

    数据库设计

    最简单的数据库设计例如以下:

    表名

    列名

    描写叙述

    类型

    t_dept

    部门表

    id

    部门编号

    Int

    name

    部门名称

    Varchar

     

    t_team

    小组表

    id

    小组编号

    Int

    name

    小组名称

    Varchar

     

    t_user

    用户表

    id

    用户编号

    Int

    username

    username称

    Varchar

    password

    用户加密password

    Varchar

    salt

    用户加密盐

    Varchar

    manager_id

    用户上级领导id

    Int

     

    t_role

    角色表

    id

    角色编号

    Int

    name

    角色名称

    Varchar

    dept_flag

    角色是否关联部门

    Bool

    team_flag

    角色是否关联小组

    Bool

     

    t_user_role

    用户所属角色表

    Id

    用户角色编号

    Int

    user_id

    用户编号

    Int

    dept_id

    部门编号

    Int

    team_id

    小组编号

    Int

    role_id

    角色编号

    Int

     

    t_resource

    资源表

    id

    资源编号

    Int

    name

    资源名称

    Varchar

     

    t_permission

    权限表

    id

    权限编号

    Int

    name

    权限名称

    Varchar

     

    t_role_resource_permission

    角色拥有的资源权限表

    id

    编号

    Int

    role_id

    角色编号

    Int

    resource_id

    资源编号

    Int

    permission_id

    权限编号

    Int

     

     

     

     

    t_leave

    请假申请表

    id

    请假申请编号

    Int

    user_id

    请假者编号

    Int

    days

    请假时长

    Int

    fromDate

    開始日期

    Date

    toDate

    结束日期

    Date

    remark

    请假说明

    Varchar

    flag

    请假状态

    Int

    apply_date

    申请的时间

    Datetime

    review_user_id

    审核人编号

    Int

    review_date

    审核日期

    Date

    review_remark

    审核说明

    Varchar

    rereview_user_id

    复核人编号

    Int

    rereview_date

    复核日期

    Date

    rereview_remark

    复核说明

    Varchar

    加入測试数据:


    项目搭建

    Maven Project: pom.xml

    新建一个maven项目,packaging设定为war,设定Spring和Shiro的版本号为3.1.4.RELEASE和1.2.3。并加入必要的依赖。

    详细例如以下:

    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    	<modelVersion>4.0.0</modelVersion>
    	<groupId>com.xx.shiro</groupId>
    	<artifactId>demo</artifactId>
    	<version>1.0.0</version>
    	<packaging>war</packaging>
    
    	<properties>
    		<java-version>1.7</java-version>
    		<org.springframework-version>3.1.4.RELEASE</org.springframework-version>
    		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    		<org.shiro-version>1.2.3</org.shiro-version>
    	</properties>
    
    	<dependencies>
    		<!-- Java EE 6 API java enterprise edition -->
    		<dependency>
    			<groupId>javax</groupId>
    			<artifactId>javaee-api</artifactId>
    			<version>6.0</version>
    			<scope>provided</scope>
    		</dependency>
    
    		<!-- spring-context [annotation...] -->
    		<dependency>
    			<groupId>org.springframework</groupId>
    			<artifactId>spring-context</artifactId>
    			<version>${org.springframework-version}</version>
    			<exclusions>
    				<!-- Exclude Commons Logging in favor of SLF4j -->
    				<exclusion>
    					<groupId>commons-logging</groupId>
    					<artifactId>commons-logging</artifactId>
    				</exclusion>
    			</exclusions>
    		</dependency>
    
    		<!-- spring modules start -->
    		<dependency>
    			<groupId>org.springframework</groupId>
    			<artifactId>spring-webmvc</artifactId>
    			<version>${org.springframework-version}</version>
    		</dependency>
    		<dependency>
    			<groupId>org.springframework</groupId>
    			<artifactId>spring-core</artifactId>
    			<version>${org.springframework-version}</version>
    		</dependency>
    
    		<dependency>
    			<groupId>org.springframework</groupId>
    			<artifactId>spring-jdbc</artifactId>
    			<version>${org.springframework-version}</version>
    		</dependency>
    
    		<dependency>
    			<groupId>org.springframework</groupId>
    			<artifactId>spring-aop</artifactId>
    			<version>${org.springframework-version}</version>
    		</dependency>
    
    		<dependency>
    			<groupId>org.springframework</groupId>
    			<artifactId>spring-tx</artifactId>
    			<version>${org.springframework-version}</version>
    		</dependency>
    		
    		<!-- apache shiro start -->
    		<dependency>
    			<groupId>org.apache.shiro</groupId>
    			<artifactId>shiro-core</artifactId>
    			<version>${org.shiro-version}</version>
    		</dependency>
    		<dependency>
    			<groupId>org.apache.shiro</groupId>
    			<artifactId>shiro-ehcache</artifactId>
    			<version>${org.shiro-version}</version>
    		</dependency>
    		<dependency>
    			<groupId>org.apache.shiro</groupId>
    			<artifactId>shiro-web</artifactId>
    			<version>${org.shiro-version}</version>
    		</dependency>
    		<dependency>
    			<groupId>org.apache.shiro</groupId>
    			<artifactId>shiro-quartz</artifactId>
    			<version>${org.shiro-version}</version>
    		</dependency>
    		<dependency>
    			<groupId>org.apache.shiro</groupId>
    			<artifactId>shiro-spring</artifactId>
    			<version>${org.shiro-version}</version>
    		</dependency>
    		<!-- apache shiro end -->
    
    		<!-- jackson -->
    		<dependency>
    			<groupId>org.codehaus.jackson</groupId>
    			<artifactId>jackson-core-asl</artifactId>
    			<version>1.8.8</version>
    		</dependency>
    		<dependency>
    			<groupId>org.codehaus.jackson</groupId>
    			<artifactId>jackson-mapper-asl</artifactId>
    			<version>1.8.8</version>
    		</dependency>
    
    		<!-- java.lang.* Extension -->
    		<dependency>
    			<groupId>commons-lang</groupId>
    			<artifactId>commons-lang</artifactId>
    			<version>2.6</version>
    		</dependency>
    
    		<!-- java.io.InputStream and Reader Extension -->
    		<dependency>
    			<groupId>commons-io</groupId>
    			<artifactId>commons-io</artifactId>
    			<version>2.4</version>
    		</dependency>
    
    		<!-- JavaServer Pages Standard Tag Library -->
    		<dependency>
    			<groupId>jstl</groupId>
    			<artifactId>jstl</artifactId>
    			<version>1.2</version>
    		</dependency>
    
    		<!-- slf4j -->
    		<dependency>
    			<groupId>org.slf4j</groupId>
    			<artifactId>slf4j-api</artifactId>
    			<version>1.6.6</version>
    		</dependency>
    		<dependency>
    			<groupId>org.slf4j</groupId>
    			<artifactId>slf4j-log4j12</artifactId>
    			<version>1.6.6</version>
    		</dependency>
    
    		<!-- log4j -->
    		<dependency>
    			<groupId>log4j</groupId>
    			<artifactId>log4j</artifactId>
    			<version>1.2.15</version>
    			<exclusions>
    				<exclusion>
    					<groupId>com.sun.jmx</groupId>
    					<artifactId>jmxri</artifactId>
    				</exclusion>
    				<exclusion>
    					<groupId>com.sun.jdmk</groupId>
    					<artifactId>jmxtools</artifactId>
    				</exclusion>
    				<exclusion>
    					<groupId>javax.jms</groupId>
    					<artifactId>jms</artifactId>
    				</exclusion>
    			</exclusions>
    		</dependency>
    
    		<!-- encode -->
    		<dependency>
    			<groupId>commons-codec</groupId>
    			<artifactId>commons-codec</artifactId>
    			<version>1.6</version>
    		</dependency>
    		<dependency>
    			<groupId>com.xx.rtpc.shared.utils</groupId>
    			<artifactId>rtpc-crypt</artifactId>
    			<version>3.1.0</version>
    		</dependency>
    
    		<dependency>
    			<groupId>c3p0</groupId>
    			<artifactId>c3p0</artifactId>
    			<version>0.9.1</version>
    		</dependency>
    
    		<dependency>
    			<groupId>mysql</groupId>
    			<artifactId>mysql-connector-java</artifactId>
    			<version>5.1.4</version>
    		</dependency>
    
    		<dependency>
    			<groupId>cglib</groupId>
    			<artifactId>cglib</artifactId>
    			<version>2.2</version>
    		</dependency>
    	</dependencies>
    
    	<build>
    		<finalName>demo</finalName>
    	</build>
    </project>
    

    配置web.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app
            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_3_0.xsd"
            version="3.0"
            metadata-complete="false">
    
        <display-name>shiro-example</display-name>
    
        <!-- Spring配置文件開始  -->
        <context-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>
                /WEB-INF/dispatcher-servlet.xml
            </param-value>
        </context-param>
    
    	<!-- listener -->
    	<listener>
    		<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
    	</listener>
    	<listener>
    		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    	</listener>
    
        <!-- shiro 安全过滤器 -->
        <filter>
            <filter-name>shiroFilter</filter-name>
            <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
            <async-supported>true</async-supported>
            <init-param>
                <param-name>targetFilterLifecycle</param-name>
                <param-value>true</param-value>
            </init-param>
        </filter>
    
        <filter-mapping>
            <filter-name>shiroFilter</filter-name>
            <url-pattern>/*</url-pattern>
            <dispatcher>REQUEST</dispatcher>
        </filter-mapping>
    
       	<servlet>
    		<servlet-name>dispatcher</servlet-name>
    		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    		<load-on-startup>1</load-on-startup>
    	</servlet>
    	<servlet-mapping>
    		<servlet-name>dispatcher</servlet-name>
    		<url-pattern>*.spring</url-pattern>
    	</servlet-mapping>
    
    	<welcome-file-list>
    		<welcome-file>/home.spring</welcome-file>
    	</welcome-file-list>
    </web-app>
    

    Spring Configure: dispatcher-servlet.xml

    在src/main/webapp/WEB-INF以下新建Spring的配置文件。命名为dispatcher-servlet.xml.详细设定例如以下:

    <?xml version="1.0" encoding="UTF-8"?

    > <beans xmlns="http://www.springframework.org/schema/beans" xmlns:util="http://www.springframework.org/schema/util" xmlns:aop="http://www.springframework.org/schema/aop" 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.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <mvc:resources location="/resources/" mapping="/resources/**" /> <context:component-scan base-package="com.xx.shiro.demo"></context:component-scan> <mvc:annotation-driven> <mvc:message-converters> <bean class="org.springframework.http.converter.StringHttpMessageConverter"> <property name="supportedMediaTypes"> <list> <value>text/html;charset=UTF-8</value> <value>text/plain;charset=UTF-8</value> </list> </property> </bean> <bean id="jsonHttpMessageConverter" class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"> <property name="supportedMediaTypes"> <list> <value>application/json</value> <value>text/json</value> </list> </property> </bean> </mvc:message-converters> </mvc:annotation-driven> <!-- 凭证匹配器 --> <bean id="credentialsMatcher" class="com.xx.shiro.demo.credentials.DemoCredentialsMatcher"> <property name="retry" value="3"/> <property name="hashAlgorithmName" value="md5"/> <property name="hashIterations" value="2"/> <property name="storedCredentialsHexEncoded" value="true"/> </bean> <!-- Realm实现 --> <bean id="xxDemoRealm" class="com.xx.shiro.demo.realm.XxDemoRealm"> <property name="credentialsMatcher" ref="credentialsMatcher"/> <property name="userService" ref="userService" /> </bean> <!-- 安全管理器 --> <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <property name="realm" ref="xxDemoRealm" /> </bean> <!-- 相当于调用SecurityUtils.setSecurityManager(securityManager) --> <bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean"> <property name="staticMethod" value="org.apache.shiro.SecurityUtils.setSecurityManager" /> <property name="arguments" ref="securityManager" /> </bean> <!-- Shiro的Web过滤器 --> <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <property name="securityManager" ref="securityManager" /> <property name="loginUrl" value="/login.spring" /> <property name="successUrl" value="/home.spring" /> <property name="filterChainDefinitions"> <value> /login.sping = anon /resources/** = anon /** = user </value> </property> </bean> <!-- Shiro生命周期处理器 --> <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" /> <!-- Enable shiro annotations --> <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor" /> <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor"> <property name="securityManager" ref="securityManager" /> </bean> <!-- 数据库连接 --> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource" /> </bean> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="com.mysql.jdbc.Driver" /> <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/shiro" /> <property name="user" value="root" /> <property name="password" value="admin" /> <property name="maxPoolSize" value="100" /> <property name="minPoolSize" value="10" /> <property name="initialPoolSize" value="10" /> <property name="maxIdleTime" value="1800" /> <property name="acquireIncrement" value="10" /> <property name="idleConnectionTestPeriod" value="600" /> <property name="acquireRetryAttempts" value="30" /> <property name="breakAfterAcquireFailure" value="false" /> <property name="preferredTestQuery" value="SELECT 1" /> </bean> <!-- User service and dao --> <bean id="userDao" class="com.xx.shiro.demo.dao.impl.UserDaoImpl"> <property name="jdbcTemplate" ref="jdbcTemplate" /> </bean> <bean id="userService" class="com.xx.shiro.demo.service.impl.UserServiceImpl"> <property name="userDao" ref="userDao" /> </bean> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/pages/"></property> <property name="suffix" value=".jsp"></property> </bean> </beans>


    从配置文件能够看出我们要实现两个重要的模块。一个是CredentialsMatcher, 另外一个叫做Realm。CredentialsMatcher是用来验证password的正确性,Realm则是来获取用户授权和认证的信息。

    <!-- 凭证匹配器 -->
        <bean id="credentialsMatcher" class="com.xx.shiro.demo.credentials.DemoCredentialsMatcher">
        	<property name="retry" value="3"/>
            <property name="hashAlgorithmName" value="md5"/>
            <property name="hashIterations" value="2"/>
            <property name="storedCredentialsHexEncoded" value="true"/>
        </bean>
        
    	<!-- Realm实现 -->
    	<bean id="xxDemoRealm" class="com.xx.shiro.demo.realm.XxDemoRealm">
    		<property name="credentialsMatcher" ref="credentialsMatcher"/>
    		<property name="userService" ref="userService" />
    	</bean>
    

    首先来看一下自己定义的Realm,



    它继承了抽象类AuthorizingRealm,同一时候AuthorizingRealm又继承了抽象类AuthenticatingRealm,所以自己定义的Realm中要实现两个抽象方法。一个是获取认证信息。另外一个是获取授权信息。详细例如以下:

    public class XxDemoRealm extends AuthorizingRealm {
    
      UserService userService;
      @Override
      protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        if (principals == null) {
          throw new AuthorizationException("PrincipalCollection method argument cannot be null.");
        }
    
        String username = (String)principals.getPrimaryPrincipal();
    
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        authorizationInfo.setRoles(userService.findRoles(username));
        authorizationInfo.setStringPermissions(userService.findPermissions(username));
        return authorizationInfo;
      }
      @Override
      protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token)
          throws AuthenticationException {
        UsernamePasswordToken upToken = (UsernamePasswordToken) token;
        String username = upToken.getUsername();
    
        if (username == null) {
          throw new AccountException("Null usernames are not allowed by this realm.");
        }
    
        String[] pwdSalt = userService.getUserPasswordAndSalt(username);
        if (pwdSalt == null) {
          throw new AccountException("No account found for user [" + username + "]");
        }
        
        return new SimpleAuthenticationInfo(username, pwdSalt[0].toCharArray(),
            ByteSource.Util.bytes(username + pwdSalt[1]), getName());
        
      }
      public void setUserService(UserService userService) {
        this.userService = userService;
      }
    }
    

    这里我们分别返回SimpleAuthenticationInfo和SimpleAuthorizationInfo。SimpleAuthenticationInfo是由username,登录password,password盐以及realm名称组成,而SimpleAuthorizationInfo继承与SimpleAuthenticationInfo。同一时候又包括了角色和权限的集合。

    在spring的配置中我们定义自己定义realm的时候指定了凭证匹配器:

    <bean id="xxDemoRealm" class="com.xx.shiro.demo.realm.XxDemoRealm">
    	<property name="credentialsMatcher" ref="credentialsMatcher"/>
    	<property name="userService" ref="userService" />
    </bean>
    

    凭证匹配器就是用来验证用户输入的帐号password和数据库中的password是否匹配。

    public class DemoCredentialsMatcher extends HashedCredentialsMatcher {
    
      // 这里应该用cache来做, 不要用Map
      private static Map<String, AtomicInteger> cache = new HashMap<String, AtomicInteger>();
    
      private int retry = 5;
    
      @Override
      public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
        String username = (String) token.getPrincipal();
        AtomicInteger retryCount = cache.get(username);
        if (retryCount == null) {
          retryCount = new AtomicInteger(0);
          cache.put(username, retryCount);
        }
        if (retryCount.incrementAndGet() > retry) {
          throw new ExcessiveAttemptsException();
        }
        boolean matches = super.doCredentialsMatch(token, info);
        if (matches) {
          cache.remove(username);
        }
        return matches;
      }
    
      public void setRetry(int retry) {
        this.retry = retry;
      }
    
    }
    

    我们这里使用的是HashedCredentialsMatcher,另外加入了重试最大次数的机制。失败几次之后就不能登录了,解锁这里没有涉及到。

    官方文档地址:http://shiro.apache.org/spring.html


    未完待续。。

    。。。


  • 相关阅读:
    动态规划-1维消消乐
    矩阵求幂-倍加算法
    动态规划-匹配问题
    动态规划-最短回文串
    动态规划-最长回文子串
    动态规划-矩形嵌套
    动态规划-硬币找零
    windows 2003最完善最完美的权限及安全设置解决方案【转】
    python模块之email: 电子邮件编码
    word页面设置问题。通过域设置首页不计算页面的自定义页码格式
  • 原文地址:https://www.cnblogs.com/blfbuaa/p/7371402.html
Copyright © 2011-2022 走看看