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

因为官方给出的文档不够具体,对新手而言通过官方文档还不可以非常快的搭建出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

未完待续。。

。。。

Shiro集成Spring的更多相关文章

  1. shiro集成spring&工作流程&DelegatingFilterProxy

    1.集成Spring 参考文献: 新建web工程: ehcache-core来自Hibernate wen.xml <?xml version="1.0" encoding= ...

  2. shiro 集成spring 配置 学习记录(一)

    首先当然是项目中需要增加shiro的架包依赖: <!-- shiro --> <dependency> <groupId>org.apache.shiro</ ...

  3. shiro 集成spring 使用 redis作为缓存 学习记录(六)

    1.在applicationContext-redis.xml配置文件中增加如下: 申明一个cacheManager对象 用来注入到  shiro的   securityManager 属性  cac ...

  4. Shiro 集成Spring 使用 redis时 使用redisTemplate替代jedisPool(五)

    1.添加依赖架包: <dependency> <groupId>org.springframework.data</groupId> <artifactId& ...

  5. shiro学习(四、shiro集成spring+springmvc)

    依赖:spring-context,spring-MVC,shiro-core,shiro-spring,shiro-web 实话实说:web.xml,spring,springmvc配置文件好难 大 ...

  6. Apache Shiro 集成Spring(二)

    1.依赖: <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-cor ...

  7. shiro与spring集成

    简介 Apache Shiro 是 Java 的一个安全(权限)框架.主要提供了认证.授权.加密和会话管理等功能. Authentication:身份认证/登录,验证用户是不是拥有相应的身份:Auth ...

  8. shiro:集成Spring(四)

    基于[加密及密码比对器(三)]项目改造 引入相关依赖环境 shiro-spring已经包含 shiro-core和shiro-web 所以这两个依赖可以删掉 <!--shiro继承spring依 ...

  9. cas+tomcat+shiro实现单点登录-4-Apache Shiro 集成Cas作为cas client端实现

    目录 1.tomcat添加https安全协议 2.下载cas server端部署到tomcat上 3.CAS服务器深入配置(连接MYSQL) 4.Apache Shiro 集成Cas作为cas cli ...

随机推荐

  1. 轻量级ORM框架Dapper应用八:使用Dapper实现DTO

    一.什么是DTO 先来看看百度百科的解释: 数据传输对象(DTO)(Data Transfer Object),是一种设计模式之间传输数据的软件应用系统.数据传输目标往往是数据访问对象从数据库中检索数 ...

  2. Java零拷贝

    1.摘要 零拷贝的“零”是指用户态和内核态间copy数据的次数为零. 传统的数据copy(文件到文件.client到server等)涉及到四次用户态内核态切换.四次copy.四次copy中,两次在用户 ...

  3. Check which .NET Framework version is installed

    his article will help you to know which .NET Framework version is installed from command line. Check ...

  4. OSPF建立邻居、邻接关系 学习笔记

    Ospf中路由器之间存在两种连接关系:邻居关系和邻接关系.本博文将详细介绍这2种关系建立及工作原理. 如果两台路由器之间共享一条公共数据链路(两台路由器中间没有其它路由器,或者两台路由器之间存在虚连接 ...

  5. Css格式化/压缩(代码)

    function $() { var elements = new Array(); for (var i = 0; i < arguments.length; i++) { var eleme ...

  6. 使用Photoshop画一个圆锥体

    一.准备工作 软件环境:PhotoshopCS6 实验目的:通过运用变换和选区工具,画出一个圆锥体 二.实验步骤 1,新建文件 2,前景色设置为黑色,并进行填充(快捷键 Alt+Delete) 3,创 ...

  7. 第三百四十一节,Python分布式爬虫打造搜索引擎Scrapy精讲—编写spiders爬虫文件循环抓取内容—meta属性返回指定值给回调函数—Scrapy内置图片下载器

    第三百四十一节,Python分布式爬虫打造搜索引擎Scrapy精讲—编写spiders爬虫文件循环抓取内容—meta属性返回指定值给回调函数—Scrapy内置图片下载器 编写spiders爬虫文件循环 ...

  8. JDBC事务提交/回滚实例

    以下是使用事务教程中描述的提交和回滚的代码示例. 此示例代码是基于前面章节中完成的环境和数据库设置编写的. 复制并将以下示例代码保存到:CommitAndRollback.java 中,编译并运行如下 ...

  9. tpshop添加后台菜单

    目前在后台公用函数文件function.php中getAllMenu方法里添加, 格式如下 array( 'system' => array('name'=>'系统设置','icon'=& ...

  10. Android开发之获取相册照片和获取拍照照片二

    转至 http://blog.csdn.net/beyond0525/article/details/8940840 上一篇文章中讲解了照相机获取照片的时候遇到了可能取得的uri为null的状态,并给 ...