以前就一直想学Shiro怎么使用,但一直没动力学,这次因为项目中要用,没办法就去学了。其实Shiro还是挺简单的,而且用着也很方便。例子是一个关于用户角色权限的例子,用户与角色,角色与权限均为多对多的关系。本次例子是Maven搭建,框架使用全注解方式。个人习惯用一些版本比较新的框架,一下是各框架版本
Spring版本:4.1.6
Hibernate版本:4.5.2
Shiro版本:1.3.2
数据连接:druid(注:其实druid和dbcp很像,基础配置基本相同)

jdbc.properties

# MySQL
hibernate.mysql.dialect=org.hibernate.dialect.MySQL5InnoDBDialect
db.mysql.driverClassName=com.mysql.jdbc.Driver
# ssh
db.mysql.ssh.url=jdbc\:mysql\://127.0.0.1\:3306/test?useUnicode\=true&characterEncoding\=utf-8
db.mysql.ssh.username=root
db.mysql.ssh.password=roothibernate.show_sql=true
hibernate.hbm2ddl.auto=update
hibernate.cache.use_second_level_cache=false
hibernate.cache.use_query_cache=false
hibernate.jdbc.batch_size=50
# DBCP property
db.dbcp.maxActive=100
db.dbcp.maxIdle=30
db.dbcp.minIdle=5
db.dbcp.maxWait=5000# DRUID property
db.druid.filters=stat
db.druid.maxActive=100
db.druid.initialSize=1
db.druid.maxIdle=300
db.druid.minIdle=5
db.druid.maxWait=5000
db.druid.timeBetweenEvictionRunsMillis=3600000
db.druid.minEvictableIdleTimeMillis=3600000
db.druid.validationQuery=SELECT 'x'
db.druid.testWhileIdle=true
db.druid.testOnBorrow=false
db.druid.testOnReturn=false
db.druid.poolPreparedStatements=true
db.druid.maxPoolPreparedStatementPerConnectionSize=50

applicationContext.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"   xmlns:p="http://www.springframework.org/schema/p"  xmlns:aop="http://www.springframework.org/schema/aop"   xmlns:context="http://www.springframework.org/schema/context"  xmlns:jee="http://www.springframework.org/schema/jee"  xmlns:tx="http://www.springframework.org/schema/tx"  xsi:schemaLocation="    http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd  http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd  http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd  http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.0.xsd  http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd"><!-- 声明自动为spring容器中那些配置@Aspectj切面的bean创建代理,织入切面 --><aop:aspectj-autoproxy></aop:aspectj-autoproxy><!-- 支持事物注解(@Transactional) --><tx:annotation-driven/><!-- 加载配置文件 --><bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"><property name="locations"><value>classpath:config/jdbc.properties</value></property></bean><!-- begin --><!-- 这几行代码原本可以放在 springmvc 的配置文件中,但在加入 Shiro 权限框架后必须放在 Spring 的配置文件中,否则在 realm 中获取不到 service --><context:annotation-config /><context:component-scan base-package="com.test"><context:include-filter type="annotation" expression="org.springframework.stereotype.Service" /><context:include-filter type="annotation" expression="org.springframework.stereotype.Repository" /></context:component-scan><!-- end --><!-- 配置sql慢的标准,超过5秒就是慢 --><bean id="stat-filter" class="com.alibaba.druid.filter.stat.StatFilter"><property name="slowSqlMillis" value="5000" /><property name="logSlowSql" value="true" /></bean><!-- sql注入攻击配置 --><bean id="wall-filter-config" class="com.alibaba.druid.wall.WallConfig" init-method="init"><!-- 指定配置装载目录 --><property name="dir" value="META-INF/druid/wall/mysql" /></bean><bean id="wall-filter" class="com.alibaba.druid.wall.WallFilter"><property name="dbType" value="mysql" /><property name="config" ref="wall-filter-config" /></bean><!-- 加载数据源 --><!-- SSH数据库 --><!-- druid连接方式 --><bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close"><property name="url" value="${db.mysql.ssh.url}"/><property name="username" value="${db.mysql.ssh.username}"/><property name="password" value="${db.mysql.ssh.password}"/><property name="maxActive" value="${db.druid.maxActive}"/><!-- 最大连接池数量 --><!-- 初始化时建立物理连接的个数。初始化发生在显示调用init方法,或者第一次getConnection时 --><property name="initialSize" value="${db.druid.initialSize}" /><property name="maxIdle" value="${db.druid.maxIdle}"/><!-- 最大空闲数,数据库连接的最大空闲时间。超出空闲时间该连接被释放 --><property name="minIdle" value="${db.druid.minIdle}"/><!-- 最小空时间 --><property name="maxWait" value="${db.druid.maxWait}"/><!-- 最大等待毫秒,单位为:ms。超出时间会出错误信息 --><!-- 有两个含义: 1) Destroy线程会检测连接的间隔时间 2) testWhileIdle的判断依据,详细看testWhileIdle属性的说明--><property name="timeBetweenEvictionRunsMillis" value="${db.druid.timeBetweenEvictionRunsMillis}" /><property name="minEvictableIdleTimeMillis" value="${db.druid.minEvictableIdleTimeMillis}" /><!-- 用来检测连接是否有效的sql,要求是一个查询语句。如果validationQuery为null,testOnBorrow、testOnReturn、 testWhileIdle都不会其作用。 --><property name="validationQuery" value="${db.druid.validationQuery}" /><!-- 建议配置为true,不影响性能,并且保证安全性。 申请连接的时候检测,如果空闲时间大于 timeBetweenEvictionRunsMillis, 执行validationQuery检测连接是否有效。 --><property name="testWhileIdle" value="${db.druid.testWhileIdle}" /><!-- 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。默认为true --><property name="testOnBorrow" value="${db.druid.testOnBorrow}" /><!-- 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能,默认为true --><property name="testOnReturn" value="${db.druid.testOnReturn}" /><!-- 是否缓存preparedStatement,也就是PSCache。PSCache对支持游标的数据库性能提升巨大,比如说oracle。 在mysql5.5以下的版本中没有PSCache功能,建议关闭掉。默认false --><property name="poolPreparedStatements" value="${db.druid.poolPreparedStatements}" /><!-- 指定每个连接上PSCache的大小 --><property name="maxPoolPreparedStatementPerConnectionSize" value="${db.druid.maxPoolPreparedStatementPerConnectionSize}" /><!--    属性类型是字符串,通过别名的方式配置扩展插件, 常用的插件有: 监控统计用的filter:stat  日志用的filter:log4j 防御sql注入的filter:wal--><property name="filters" value="${db.druid.filters}"/><property name="proxyFilters"><list><ref bean="stat-filter"/><ref bean="wall-filter"/></list></property></bean><bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean"><property name="dataSource" ref="dataSource"/><property name="packagesToScan"><list><value>com.test.entity</value></list></property><property name="hibernateProperties"><props><!-- 方言 --><prop key="hibernate.dialect">${hibernate.mysql.dialect}</prop><!-- 是否打印SQL语句 --><prop key="hibernate.show_sql">${hibernate.show_sql}</prop><!-- 是否开启二级缓存 --><prop key="hibernate.cache.use_second_level_cache">${hibernate.cache.use_second_level_cache}</prop><!-- 是否开启缓存查询 --><prop key="hibernate.cache.use_query_cahe">${hibernate.cache.use_query_cache}</prop><!-- 数据库批量增删改操作的最大数 --><prop key="hibernate.jdbc.batch_size">${hibernate.jdbc.batch_size}</prop><!-- 设置自动更新|创建|验证表结构 --><prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl.auto}</prop><!-- hiberante4.x的session创建方式 --><!-- 使用本地事物 --><prop key="current_session_context_class">thead</prop><!-- 使用全局事物 --><!-- <prop key="current_session_context_class">jta</prop> --><!--<prop key="hibernate.query.factory_class">org.hibernate.hql.ast.ASTQueryTranslatorFactory</prop>--></props></property></bean><!-- 声明式事物 --><bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager"><property name="sessionFactory" ref="sessionFactory"/></bean><!-- 配置事物异常封装 --><bean id="persistenceExceptionTranslationPostProcessor" class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/><aop:config><aop:pointcut id="productServiceMethods" expression="execution(* com.test.service.*.*(..))" /><aop:advisor advice-ref="txAdvice" pointcut-ref="productServiceMethods" /></aop:config><tx:advice id="txAdvice" transaction-manager="transactionManager"><tx:attributes><tx:method name="save*" propagation="REQUIRED"/><tx:method name="add*" propagation="REQUIRED"/><tx:method name="create*" propagation="REQUIRED"/><tx:method name="insert*" propagation="REQUIRED"/><tx:method name="update*" propagation="REQUIRED"/><tx:method name="del*" propagation="REQUIRED"/><tx:method name="remove*" propagation="REQUIRED"/><tx:method name="destroy*" propagation="REQUIRED"/><tx:method name="*" propagation="SUPPORTS" read-only="true"/></tx:attributes></tx:advice><!-- 必须加入 --><!-- 注解事物 --><tx:annotation-driven transaction-manager="transactionManager"/>
</beans>

这里要特别说明一下

<context:annotation-config /><context:component-scan base-package="com.test"><context:include-filter type="annotation" expression="org.springframework.stereotype.Service" /><context:include-filter type="annotation" expression="org.springframework.stereotype.Repository" /></context:component-scan>

这段代码在不是用Shiro的时候,放在SpringMVC的配置文件中也是可以的,但使用了Shiro后必须放在Spring的配置文件中,具体原因现在不是很清楚

springmvc-servlet.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" 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:mvc="http://www.springframework.org/schema/mvc"xsi:schemaLocation="http://www.springframework.org/schema/beans  http://www.springframework.org/schema/beans/spring-beans-4.0.xsd  http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd  http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd  http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsdhttp://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd"><!-- 避免异步后中文乱码 --><bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"><property name="messageConverters"><list><bean id="stringHttpMessageConverter" class="org.springframework.http.converter.StringHttpMessageConverter"><property name="writeAcceptCharset" value="false"/><!-- 避免响应头过大 --><property name="supportedMediaTypes"><list><value>text/html;charset=UTF-8</value><value>text/json;charset=UTF-8</value><value>application/json;charset=UTF-8</value><value>application/xml;charset=UTF-8</value></list></property></bean></list></property></bean><!-- <context:component-scan base-package="com.test.contorller" />  --><context:component-scan base-package="com.test.contorller" /> <!-- 添加注解驱动 --><mvc:annotation-driven/><!-- 视图解析器 --><bean id="viewResolver"class="org.springframework.web.servlet.view.InternalResourceViewResolver"><property name="prefix" value="/" /><property name="suffix" value=".jsp" /></bean><!-- 上传文件相关的配置 --><bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"><property name="defaultEncoding" value="utf-8" /><property name="maxUploadSize" value="104857600" /><property name="maxInMemorySize" value="4096" /></bean></beans>

applicationContext-shiro.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"   xmlns:aop="http://www.springframework.org/schema/aop"   xmlns:context="http://www.springframework.org/schema/context"  xmlns:tx="http://www.springframework.org/schema/tx"  xmlns:cache="http://www.springframework.org/schema/cache"  xsi:schemaLocation="    http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd  http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd  http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd  http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsdhttp://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache-4.0.xsd"><!--自定义的Roles Filter--><!-- 注意:下面如果这样配置:/**/del/**=roles["管理员,总经理"] Shiro 默认表示用户同时具有这两个角色才能访问,但我们往往需要只要满足其中一个即可,这时就需要重写拦截器 --><bean id="anyRoles" class="com.test.shiro.CustomRolesAuthorizationFilter" /><bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"><property name="securityManager" ref="securityManager"/><property name="loginUrl" value="/login.jsp"/><property name="successUrl" value="/inde.jsp"/><property name="unauthorizedUrl" value="/403.jsp"/><!-- 具体配置需要拦截哪些 URL, 以及访问对应的 URL 时使用 Shiro 的什么 Filter 进行拦截  --><!-- 这里对路径中包含 del 的请求地址只有拥有“管理员”或“总经理”角色的用户才能访问 --><property name="filterChainDefinitions"><value>/index.jsp=authc/menu.jsp=authc/**/del/**=anyRoles["管理员,总经理"]/logout.jsp = logout</value></property></bean><!-- 配置进行授权和认证的 Realm --><bean id="myRealm" class="com.testbt.shiro.ShiroDbRealm"><!-- MD5加密 --><property name="credentialsMatcher"><bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher"><property name="hashAlgorithmName" value="MD5"></property> <!-- 加密算法的名称 --><property name="hashIterations" value="32"></property> <!-- 配置加密的次数 --><!-- true:指定Hash散列值使用Hex加密存;false:表明hash散列值用用Base64-encoded存储 --><property name="storedCredentialsHexEncoded" value="true"/></bean></property></bean><!-- 配置 Shiro 的 SecurityManager Bean. --><bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"><property name="cacheManager" ref="cacheManager"/><property name="realm" ref="myRealm"/><property name="sessionMode" value="native"/></bean><!-- 配置 Bean 后置处理器: 会自动的调用和 Spring 整合后各个组件的生命周期方法. --><bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/><!-- 配置缓存管理器 --><bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager"><property name="cacheManager" ref="cacheManagerEhcache"/></bean><!-- cacheManager, 指定ehcache-shiro.xml的位置 --> <bean id="cacheManagerEhcache" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"><property name="configLocation"><value>classpath:config/ehcache-shiro.xml</value></property><!-- 由于hibernate也使用了Ehcache, 保证双方都使用同一个缓存管理器 --><property name="shared" value="true"/></bean></beans>

ehcache-shiro.xml

<?xml version="1.0" encoding="UTF-8"?>
<ehcache updateCheck="false"  name="shirocache"><diskStore path="java.io.tmpdir"/><!-- 登录记录缓存 锁定10分钟 --><cache name="passwordRetryCache"maxEntriesLocalHeap="2000"eternal="false"timeToIdleSeconds="3600"timeToLiveSeconds="0"overflowToDisk="false"statistics="true"></cache><cache name="authorizationCache"maxEntriesLocalHeap="2000"eternal="false"timeToIdleSeconds="3600"timeToLiveSeconds="0"overflowToDisk="false"statistics="true"></cache><cache name="authenticationCache"maxEntriesLocalHeap="2000"eternal="false"timeToIdleSeconds="3600"timeToLiveSeconds="0"overflowToDisk="false"statistics="true"></cache><cache name="shiro-activeSessionCache"maxEntriesLocalHeap="2000"eternal="false"timeToIdleSeconds="3600"timeToLiveSeconds="0"overflowToDisk="false"statistics="true"></cache><cache name="shiro_cache"maxElementsInMemory="2000"maxEntriesLocalHeap="2000"eternal="false"timeToIdleSeconds="0"timeToLiveSeconds="0"maxElementsOnDisk="0"overflowToDisk="true"memoryStoreEvictionPolicy="FIFO"statistics="true"></cache>
</ehcache>

不需要缓存的可以直接把applicationContext-shiro.xml中关于配置缓存的代码删掉即可

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns="http://java.sun.com/xml/ns/javaee"xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"id="WebApp_ID" version="3.0"><display-name>SSH</display-name><!-- Spring过滤器 --><filter><filter-name>CharacterEncodingFilter</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><context-param><param-name>webAppRootKey</param-name><param-value>SSH</param-value></context-param><filter-mapping><filter-name>CharacterEncodingFilter</filter-name><url-pattern>/*</url-pattern></filter-mapping><filter><filter-name>openSessionInViewFilter</filter-name><filter-class>org.springframework.orm.hibernate4.support.OpenSessionInViewFilter</filter-class></filter><filter-mapping><filter-name>openSessionInViewFilter</filter-name><url-pattern>/*</url-pattern></filter-mapping><context-param><param-name>contextConfigLocation</param-name><param-value>classpath:config/applicationContext.xmlclasspath:config/applicationContext-shiro.xml</param-value></context-param><listener><listener-class>org.springframework.web.context.ContextLoaderListener</listener-class></listener><servlet><servlet-name>springmvc</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><init-param><param-name>contextConfigLocation</param-name><param-value>classpath:config/springmvc-servlet.xml</param-value></init-param><load-on-startup>1</load-on-startup></servlet><servlet-mapping><servlet-name>springmvc</servlet-name><url-pattern>*.htl</url-pattern></servlet-mapping><filter><filter-name>shiroFilter</filter-name><filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class></filter><filter-mapping><filter-name>shiroFilter</filter-name><url-pattern>/*</url-pattern></filter-mapping><error-page><error-code>404</error-code><location>/404.html</location></error-page><error-page><error-code>500</error-code><location>/500.html</location></error-page><welcome-file-list><welcome-file>login.jsp</welcome-file></welcome-file-list>
</web-app>

实体类

UserInfo.java

package com.test.entity;import java.util.ArrayList;
import java.util.List;import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.Table;
import javax.persistence.Transient;
/*** UserInfo entity. @author Wangbt*/
@Entity
@Table(name = "user_info", catalog = "test")
public class UserInfo implements java.io.Serializable {// Fields/*** */private static final long serialVersionUID = 40482158759345958L;private String id;private String account;private String password;private String userName;private Integer age;private Integer sex;private List<Role> roles;// Constructors/** default constructor */public UserInfo() {}/** minimal constructor */public UserInfo(String id, String account, String password) {this.id = id;this.account = account;this.password = password;}/** full constructor */public UserInfo(String id, String account, String password,String userName, Integer age, Integer sex, List<Role> roles) {this.id = id;this.account = account;this.password = password;this.userName = userName;this.age = age;this.sex = sex;this.roles = roles;}// Property accessors@Id@Column(name = "id", unique = true, nullable = false, length = 36)public String getId() {return this.id;}public void setId(String id) {this.id = id;}@Column(name = "account", nullable = false, length = 36)public String getAccount() {return this.account;}public void setAccount(String account) {this.account = account;}@Column(name = "password", nullable = false, length = 18)public String getPassword() {return this.password;}public void setPassword(String password) {this.password = password;}@Column(name = "user_name", length = 225)public String getUserName() {return this.userName;}public void setUserName(String userName) {this.userName = userName;}@Column(name = "age")public Integer getAge() {return this.age;}public void setAge(Integer age) {this.age = age;}@Column(name = "sex")public Integer getSex() {return this.sex;}public void setSex(Integer sex) {this.sex = sex;}@ManyToMany(cascade=CascadeType.REFRESH,fetch=FetchType.EAGER)@JoinTable(name = "user_role",joinColumns = {@JoinColumn(name = "user_id")},inverseJoinColumns = {@JoinColumn(name = "role_id")})public List<Role> getRoles() {return roles;}public void setRoles(List<Role> roles) {this.roles = roles;}@Transientpublic List<String> getRoleName() {List<Role> roles = getRoles();List<String> list = new ArrayList<String>();for (Role role : roles) {list.add(role.getRoleName());}return list;}}

Role.java

package com.test.entity;import java.util.ArrayList;
import java.util.List;import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.Table;
import javax.persistence.Transient;/*** Role entity. @author Wangbt*/
@Entity
@Table(name = "role", catalog = "test")
public class Role implements java.io.Serializable {// Fields/*** */private static final long serialVersionUID = 6439880715734914582L;private String id;private String roleName;private String parentId;private String describe;private List<UserInfo> userInfos;private List<Authority> authorities;// Constructors/** default constructor */public Role() {}/** minimal constructor */public Role(String id) {this.id = id;}/** full constructor */public Role(String id, String roleName, String parentId, String describe,List<UserInfo> userInfos, List<Authority> authorities) {this.id = id;this.roleName = roleName;this.parentId = parentId;this.describe = describe;this.userInfos = userInfos;this.authorities = authorities;}// Property accessors@Id@Column(name = "id", unique = true, nullable = false, length = 36)public String getId() {return this.id;}public void setId(String id) {this.id = id;}@Column(name = "role_name", length = 225)public String getRoleName() {return this.roleName;}public void setRoleName(String roleName) {this.roleName = roleName;}@Column(name = "parent_id", length = 26)public String getParentId() {return this.parentId;}public void setParentId(String parentId) {this.parentId = parentId;}@Column(name = "describe", length = 225)public String getDescribe() {return this.describe;}public void setDescribe(String describe) {this.describe = describe;}@ManyToMany@JoinTable(name = "user_role",joinColumns = {@JoinColumn(name = "role_id")},inverseJoinColumns = {@JoinColumn(name = "user_id")})public List<UserInfo> getUserInfos() {return userInfos;}public void setUserInfos(List<UserInfo> userInfos) {this.userInfos = userInfos;}@ManyToMany@JoinTable(name = "role_authority",joinColumns = {@JoinColumn(name = "role_id")},inverseJoinColumns = {@JoinColumn(name = "authority_id")})public List<Authority> getAuthorities() {return authorities;}public void setAuthorities(List<Authority> authorities) {this.authorities = authorities;}@Transientpublic List<String> getAuthorityName() {List<String> list = new ArrayList<String>();List<Authority> authorities = getAuthorities();for (Authority auth : authorities) {list.add(auth.getAuthName());}return list;}}

Authority.java

package com.test.entity;import java.util.List;import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.Table;/*** Authority entity. @author Wangbt*/
@Entity
@Table(name = "authority", catalog = "test")
public class Authority implements java.io.Serializable {/*** */private static final long serialVersionUID = 4091626000965414186L;// Fieldsprivate String id;private String authName;private String parenntId;private List<Role> roles;// Constructors/** default constructor */public Authority() {}/** minimal constructor */public Authority(String id) {this.id = id;}/** full constructor */public Authority(String id, String authName, String parenntId, List<Role> roles) {this.id = id;this.authName = authName;this.parenntId = parenntId;this.roles = roles;}// Property accessors@Id@Column(name = "id", unique = true, nullable = false, length = 36)public String getId() {return this.id;}public void setId(String id) {this.id = id;}@Column(name = "auth_name", length = 225)public String getAuthName() {return this.authName;}public void setAuthName(String authName) {this.authName = authName;}@Column(name = "parennt_id", length = 36)public String getParenntId() {return this.parenntId;}public void setParenntId(String parenntId) {this.parenntId = parenntId;}@ManyToMany@JoinTable(name = "role_authority",joinColumns = {@JoinColumn(name = "authority_id")},inverseJoinColumns = {@JoinColumn(name = "role_id")})public List<Role> getRoles() {return roles;}public void setRoles(List<Role> roles) {this.roles = roles;}
}

Dao层

UserInfoDao.java

import com.test.entity.UserInfo;public interface UserInfoDao {public UserInfo findAccount(String account);
}

UserInfoDaoImpl.java

package com.test.dao.impl;import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;import com.test.dao.UserInfoDao;
import com.test.entity.UserInfo;
import com.test.util.BaseDao;@Repository
public class UserInfoDaoImpl extends BaseDao<UserInfo, String> implements UserInfoDao {@Autowiredprivate SessionFactory sessionFactory;/*** @return session*/public Session getSession(){Session session = this.sessionFactory.getCurrentSession();return session;}@Overridepublic UserInfo findAccount(String account) {String hql = "from UserInfo where 1 = 1 and account = ?";Query query = this.getSession().createQuery(hql);query.setParameter(0, account);UserInfo userInfo = (UserInfo) query.uniqueResult();return userInfo;}}

Service层

UserInfoService.java

package com.test.service;import com.test.entity.UserInfo;public interface UserInfoService {public UserInfo userLogin(String account);
}

UserInfoServiceImpl.java

package com.test.service.impl;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import com.test.dao.UserInfoDao;
import com.test.entity.UserInfo;
import com.test.service.UserInfoService;@Service("userInfoService")
public class UserInfoServiceImpl implements UserInfoService {@Autowiredprivate UserInfoDao userInfoDao;@Overridepublic UserInfo login(String account) {UserInfo user = this.userInfoDao.login(account);if (user != null) {return user;}return null;}}

接下来是最为关键的Realm的实现,用户的角色、权限主要通过这个类控制

ShiroDbRealm.java

package com.test.shiro;import java.util.ArrayList;
import java.util.List;import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;import com.test.entity.Role;
import com.test.entity.UserInfo;
import com.test.service.UserInfoService;
import com.test.util.CryptographyUtil;public class ShiroDbRealm extends AuthorizingRealm {@Autowiredprivate UserInfoService userInfoService;public static final String SESSIOIN_USER_KEY = "userInfo";/*** 授权查询回调函数,进行鉴权但缓存中无用户的授权信息时调用,负责在应用程序中决定用户访问控制的方法*/@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {// 获取登录时输入的帐号//String account = (String) principalCollection.fromRealm(getName()).iterator().next();UserInfo userInfo = (UserInfo) SecurityUtils.getSubject().getSession().getAttribute(ShiroDbRealm.SESSIOIN_USER_KEY);SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();// 设置角色info.addRoles(userInfo.getRoleName());List<Role> roles = userInfo.getRoles();List<String> auths = new ArrayList<>();for (Role role : roles) {List<String> list = role.getAuthority();list.removeAll(auths);auths.addAll(list);}// 设置权限info.addStringPermissions(auths);return info;}/*** 认证回调函数,登录信息和用户信息验证*/@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authToken) throws AuthenticationException {// 把 token 转成 userUserInfo userInfo = tokenToUser((UsernamePasswordToken)authToken);// 验证用户是否可以登录// hashIterations 必须与配置文件中的一致,否则认证失败String pwd = CryptographyUtil.md5(userInfo.getPassword(), "feicui", 32);UserInfo user = userInfoService.login(userInfo.getAccount());if (user == null)return null;// 找不到数据// 设置 sessionSession session = SecurityUtils.getSubject().getSession();session.setAttribute(ShiroDbRealm.SESSIOIN_USER_KEY, user);// 当前 Realm 的 nameString realmName = this.getName();// 登录的主要信息:可以是一个实体类对象,但实体类的对象一定是根据 token 的 username 查询得到的// Object principal = user.getAccount();Object principal = authToken.getPrincipal();// Shiro 通过 credentialsSalt 值给原始密码加密,最终比较两次密码是否一致ByteSource credentialsSalt = ByteSource.Util.bytes("feicui");return new SimpleAuthenticationInfo(principal, user.getPassword(), credentialsSalt, realmName);}/*** 从 UsernamePasswordToken 中获取用户信息* @param authToken* @return*/private UserInfo tokenToUser(UsernamePasswordToken authToken) {UserInfo user = new UserInfo();user.setAccount(authToken.getUsername());user.setPassword(String.valueOf(authToken.getPassword()));return user;}
}

CustomRolesAuthorizationFilter.java

package com.test.shiro;import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authz.AuthorizationFilter;/*** 重写 Shiro 拦截器* @author Wangbt**/
public class CustomRolesAuthorizationFilter extends AuthorizationFilter {@Overrideprotected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object object) throws Exception {Subject subject = getSubject(request, response);String[] rolesArray = (String[]) object;if (rolesArray == null || rolesArray.length == 0) //没有角色限制,有权限访问return true;for (int i = 0; i < rolesArray.length; i++) { //若当前用户是rolesArray中的任何一个,则有权限访问if (subject.hasRole(rolesArray[i])) return true;}return false;}}

UserInfoController.java

package com.test.contorller;import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;import com.test.entity.UserInfo;
import com.test.service.UserInfoService;@Controller
@RequestMapping(value = "/user/info", method = RequestMethod.POST)
public class UserInfoController {@Autowiredprivate UserInfoService userInfoService;/*** 登录* @param account* @param pwd* @return*/@RequestMapping(value = "/login")public String login(UserInfo userInfo, Model model) {if (SecurityUtils.getSubject().getSession() != null)SecurityUtils.getSubject().logout();String res = loginUser(userInfo);if (!"SUCC".equals(res)) {model.addAttribute("msg", res);return "redirect:/login.jsp";}return "redirect:/index.jsp";}/*** 退出* @return*/@RequestMapping("/logout")public String logout() {Subject subject = SecurityUtils.getSubject();subject.logout();return "login";}private String loginUser(UserInfo user) {if (isRelogin(user)) return "SUCC";// 如果已经登录,则无需再登录return shiroLogin(user);}private String shiroLogin(UserInfo user) {// 组装 token,包括用户名、密码、角色、权限等等UsernamePasswordToken token = new UsernamePasswordToken(user.getAccount(), user.getPassword().toCharArray(), null);token.setRememberMe(true);// shiro 验证登录try {SecurityUtils.getSubject().login(token);;} catch (UnknownAccountException ex){return "帐号或密码错误";} catch (IncorrectCredentialsException ex){return "帐号不存在或者密码错误";} catch (AuthenticationException ex) {return ex.getMessage();} catch (Exception e) {return "内部错误,请重新尝试";}return "SUCC";}private boolean isRelogin(UserInfo user) {Subject subject = SecurityUtils.getSubject();return subject.isAuthenticated();// true:参数未改变,无需重新登录,默认为已经登录成功;false:需重新登录}}

后台代码总算是完了,接下来就是测试了

success.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"pageEncoding="UTF-8"%>
<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
<% String path = request.getContextPath(); %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body><h1>提示信息:${msg }</h1><h1>帐号:${account }</h1><shiro:hasAnyRoles name="user"><a href="<%=path %>/user.jsp">User Page</a></shiro:hasAnyRoles><shiro:hasAnyRoles name="admin"><a href="<%=path %>/admin.jsp">Admin Page</a></shiro:hasAnyRoles><shiro:hasPermission name="改"><a href="<%=path %>/admin.jsp">auth Page</a></shiro:hasPermission>
</body>
</html>

登录成功后会调转到 success.jsp 页面,不同角色和权限的用户看到的连接也不同,获知直接访问这些页面,没有权限的用户会跳转到默认的页面,给出相应的提示。最后上一张项目结构图

下载地址: http://download.csdn.net/download/cainiao_accp/9945944
示例代码和下载代码有所不同,但下载代码比示例代码要更加严谨

Spring + SpringMVC + Hibernate + Shiro整合相关推荐

  1. Spring+SpringMVC+Hibernate+Shiro框架

    首先,web.xml配置: <?xml version="1.0" encoding="UTF-8" ?> <web-app xmlns:xs ...

  2. Spring+springmvc+hibernate+redis整合配置文件

    1.首先在eclipse中利用maven创建web项目 pom.xml文件 <project xmlns="http://maven.apache.org/POM/4.0.0" ...

  3. Spring+SpringMvc+Hibernate(SSH)+bootstrap/Jsp整合

    文章目录 前言: 准备工作: 最终项目的目录结构: 源码 一.使用idea创建web app项目 二.导包 三.SpringMVC配置 第一步:配置spring-mvc.xml 第二步:配置 web. ...

  4. spring+springmvc+hibernate 整合

    三大框架反反复复搭了很多次,虽然每次都能搭起来,但是效率不高.最近重新搭了一次,理顺了思路,整理了需要注意的地方,分享出来. 工具:Eclipse(jdk 1.7) spring和hibernate版 ...

  5. Spring+SpringMVC+Hibernate整合(封装CRUD操作)

    前言:当前Web项目开发的框架主流应该非Spring+SpringMVC+Hibernate莫属,不管是工作还是学习中涉及框架技术,首先是要搭建一套运行环境,虽然网上框架整合的教程很多,但我还是输出此 ...

  6. Spring+SpringMVC+Hibernate整合操作数据库 概述

    概述 Hibernate是一款优秀的ORM框架,能够连接并操作数据库,包括保存和修改数据.Spring MVC是Java的web框架,能够将Hibernate集成进去,完成数据的CRUD.Hibern ...

  7. Spring+SpringMvc+Hibernate 框架搭建

    2019独角兽企业重金招聘Python工程师标准>>> 框架篇:Spring+SpringMVC+hibernate整合开发 一.建立项目 1.新建一个空项目project 相当于e ...

  8. 基于Spring+SpringMvc+Hibernate的咔咔发廊管理系统

    基于Spring+SpringMvc+Hibernate的咔咔发廊管理系统 基于Spring+SpringMvc+Hibernate的家政服务网-java家政服务网 1.包含源程序,数据库脚本.代码和 ...

  9. Struts2与Spring、Hibernate三者整合的过程示例

    转载地址:http://www.360doc.com/content/09/0416/09/61497_3148602.shtml# 原来spring配置文件自动生成数据源和整合先后有关系,留着做个提 ...

最新文章

  1. 算法 - 时间复杂度
  2. Java精选笔记_XML基础
  3. 六十六、丑数系列,丑的颠覆我的思想
  4. linux cpu softirq,linux softirq机制
  5. ES6 解构赋值详解
  6. TCGA三个在线可视化网站
  7. Spring-core-ClassUtils类
  8. Saltstack远程执行命令(3)
  9. java 学习笔记之AtomicInteger类的使用
  10. Robocode学习Java
  11. Oracle 分组求和(特殊处理)
  12. 威信游戏小程序源码-合成大西瓜小游戏(合成版)源码 附带流量主功能
  13. 华为 - 路由交换基础
  14. mysql候选关键字_MySQL(三)之SQL语句分类、基本操作、三大范式
  15. Django2.0-表单(2)-表单的FIeld,验证器,提取错误字段
  16. Cannot resolve MVC View ‘XXX‘问题解决(路径跳转不过去)
  17. Sorry, name can only contain URL-friendly characters and name can no longer contain capital letters
  18. 传感网复习(一)概要、组织结构
  19. 南邮ctf 480小时精通C++
  20. Chrome 添加 fluent钱包插件

热门文章

  1. 生产计划排程为什么会这么难?
  2. 在WIN7、WIN10操作系统用WebDAV映射网络驱动器需要的操作
  3. java什么是reference_如何理解java中的Reference和引用类型?正确的案例讲解
  4. 润乾打印控制解决方案
  5. 关于Random(47)与randon.nextInt(100)的区别
  6. 分辨率计算机英语,各种分辨率的英文缩写
  7. Linux操作系统学习
  8. slamugv小车使用说明--1材料准备
  9. 如何注册自己的企业邮箱?
  10. [算法]Floyd-Warshall算法理解