序:
關於標題:
說是教學,實在愧不敢當,但苦與本人文筆有限,實在找不到更合理,謙遜的詞語表達,只能先這樣定義了。
其實最真實的想法,只是希望這個關鍵詞能讓更多的人瀏覽到這篇文章,也算是對於自己寫文章的一個肯定吧。^_^!
關於內容:
再寫這系列文章之前,本人和許多人一樣都是伸手黨,並深深的了解咱伸手黨且英文較差的朋友對於新知識的學習及獲取中文資料少的痛苦。所以本着“取之於民,共享與民”的原則,記錄下實際工作中對SpringMVC+Shiro整合應用的部分心得。本人技術水平有限,僅希望文章對他人有一定的參考價值,足矣。
關於拍磚:
請輕拍,很痛的。且最好附上您的高見。
另:Shiro基礎及原理,推薦學習http://kdboy.iteye.com/category/35212,同時感謝他的博客,在我學習Shiro的過程中,給予很大幫助!
教學:
一、SpringMVC+Apache Shiro+JPA(hibernate)整合配置
(1)新建Web工程,且導入所需Jar包。(以下截圖為真實項目中刪減后保留,如有不需要的JAR包,請自行刪除)
(2)配置web.xml,applicationContext.xml, spring-mvc.xml, log4j.properties
web.xml
1 <?xml version="1.0" encoding="UTF-8"?> 2 <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0"> 3 4 <context-param> 5 <param-name>contextConfigLocation</param-name> 6 <param-value>classpath:applicationContext.xml</param-value> 7 </context-param> 8 9 <listener> 10 <listener-class> 11 org.springframework.web.context.ContextLoaderListener 12 </listener-class> 13 </listener> 14 15 <!-- 配置spring管理OpenEntityManagerInViewFilter--> 16 <filter> 17 <filter-name>hibernateFilter</filter-name> 18 <filter-class> 19 org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter 20 </filter-class> 21 </filter> 22 <filter-mapping> 23 <filter-name>hibernateFilter</filter-name> 24 <url-pattern>/*</url-pattern> 25 </filter-mapping> 26 27 <!-- Shiro filter --> 28 <filter> 29 <filter-name>shiroFilter</filter-name> 30 <filter-class> 31 org.springframework.web.filter.DelegatingFilterProxy 32 </filter-class> 33 <init-param> 34 <param-name>targetFilterLifecycle</param-name> 35 <param-value>true</param-value> 36 </init-param> 37 </filter> 38 <filter-mapping> 39 <filter-name>shiroFilter</filter-name> 40 <url-pattern>/*</url-pattern> 41 </filter-mapping> 42 43 <!-- 配置Log4j --> 44 <context-param> 45 <param-name>webAppRootKey</param-name> 46 <param-value>spring_springmvc_jpa.root</param-value> 47 </context-param> 48 <context-param> 49 <param-name>log4jConfigLocation</param-name> 50 <param-value>classpath:log4j.properties</param-value> 51 </context-param> 52 <listener> 53 <listener-class> 54 org.springframework.web.util.Log4jConfigListener 55 </listener-class> 56 </listener> 57 58 <!-- 配置編碼過濾器 --> 59 <filter> 60 <filter-name>characterEncodingFilter</filter-name> 61 <filter-class> 62 org.springframework.web.filter.CharacterEncodingFilter 63 </filter-class> 64 <init-param> 65 <param-name>encoding</param-name> 66 <param-value>UTF-8</param-value> 67 </init-param> 68 <init-param> 69 <param-name>forceEncoding</param-name> 70 <param-value>true</param-value> 71 </init-param> 72 </filter> 73 <filter-mapping> 74 <filter-name>characterEncodingFilter</filter-name> 75 <url-pattern>/*</url-pattern> 76 </filter-mapping> 77 78 <filter> 79 <filter-name>HiddenHttpMethodFilter</filter-name> 80 <filter-class> 81 org.springframework.web.filter.HiddenHttpMethodFilter 82 </filter-class> 83 </filter> 84 <filter-mapping> 85 <filter-name>HiddenHttpMethodFilter</filter-name> 86 <servlet-name>dispatcherServlet</servlet-name> 87 </filter-mapping> 88 89 <!-- Spring 刷新Introspector防止內存泄露 --> 90 <listener> 91 <listener-class> 92 org.springframework.web.util.IntrospectorCleanupListener 93 </listener-class> 94 </listener> 95 96 97 <!-- SpringMVC核心分發器 --> 98 <servlet> 99 <servlet-name>dispatcherServlet</servlet-name> 100 <servlet-class> 101 org.springframework.web.servlet.DispatcherServlet 102 </servlet-class> 103 <init-param> 104 <param-name>contextConfigLocation</param-name> 105 <param-value>classpath:spring-mvc.xml</param-value> 106 </init-param> 107 <load-on-startup>1</load-on-startup> 108 </servlet> 109 <!-- 覆蓋default servlet的/, springmvc servlet將處理原來處理靜態資源的映射 --> 110 <servlet-mapping> 111 <servlet-name>dispatcherServlet</servlet-name> 112 <url-pattern>/</url-pattern> 113 </servlet-mapping> 114 115 <jsp-config> 116 <jsp-property-group> 117 <display-name>JSPConfiguration</display-name> 118 <url-pattern>*.jsp</url-pattern> 119 <el-ignored>true</el-ignored> 120 <page-encoding>utf-8</page-encoding> 121 <scripting-invalid>false</scripting-invalid> 122 </jsp-property-group> 123 </jsp-config> 124 125 <welcome-file-list> 126 <welcome-file>login.jsp</welcome-file> 127 </welcome-file-list> 128 129 </web-app>
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:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p" xmlns:cache="http://www.springframework.org/schema/cache" xmlns:jaxws="http://cxf.apache.org/jaxws" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache-3.1.xsd http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd "> <!-- 注解支持 --> <context:annotation-config /> <!-- 啟動組件掃描,排除@Controller組件,該組件由SpringMVC配置文件掃描 --> <context:component-scan base-package="org.shiro.demo"> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" /> </context:component-scan> <!-- 屬性文件位置 --> <context:property-placeholder location="classpath:jdbc.properties" /> <!-- 數據源 --> <bean id="dataSource" class="com.jolbox.bonecp.BoneCPDataSource" destroy-method="close"> <!-- 數據庫驅動 --> <property name="driverClass" value="${jdbc.driverClassName}" /> <!-- 相應驅動的jdbcUrl--> <property name="jdbcUrl" value="${jdbc.url}" /> <!-- 數據庫的用戶名 --> <property name="username" value="${jdbc.username}" /> <!-- 數據庫的密碼 --> <property name="password" value="${jdbc.password}" /> <!-- 檢查數據庫連接池中空閑連接的間隔時間,單位是分,默認值:240,如果要取消則設置為0 --> <property name="idleConnectionTestPeriod" value="${BoneCP.idleConnectionTestPeriod}" /> <!-- 連接池中未使用的鏈接最大存活時間,單位是分,默認值:60,如果要永遠存活設置為0 --> <property name="idleMaxAge" value="${BoneCP.idleMaxAge}" /> <!-- 每個分區最大的連接數 --> <property name="maxConnectionsPerPartition" value="${BoneCP.maxConnectionsPerPartition}" /> <!-- 每個分區最小的連接數 --> <property name="minConnectionsPerPartition" value="${BoneCP.minConnectionsPerPartition}" /> <!-- 分區數 ,默認值2,最小1,推薦3-4,視應用而定 --> <property name="partitionCount" value="${BoneCP.partitionCount}" /> <!-- 每次去拿數據庫連接的時候一次性要拿幾個,默認值:2 --> <property name="acquireIncrement" value="${BoneCP.acquireIncrement}" /> <!-- 緩存prepared statements的大小,默認值:0 --> <property name="statementsCacheSize" value="${BoneCP.statementsCacheSize}" /> <!-- 每個分區釋放鏈接助理進程的數量,默認值:3,除非你的一個數據庫連接的時間內做了很多工作,不然過多的助理進程會影響你的性能 --> <property name="releaseHelperThreads" value="${BoneCP.releaseHelperThreads}" /> </bean> <!-- JPA實體管理器工廠 --> <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="persistenceProvider" ref="persistenceProvider" /> <property name="jpaVendorAdapter" ref="jpaVendorAdapter" /> <property name="jpaDialect" ref="jpaDialect" /> <property name="packagesToScan" value="org.shiro.demo.entity" /> <property name="jpaProperties"> <props> <prop key="hibernate.dialect"> org.hibernate.dialect.MySQL5Dialect </prop> <prop key="hibernate.connection.driver_class"> com.mysql.jdbc.Driver </prop> <prop key="hibernate.max_fetch_depth">3</prop> <prop key="hibernate.jdbc.fetch_size">18</prop> <prop key="hibernate.jdbc.batch_size">10</prop> <prop key="hibernate.hbm2ddl.auto">update</prop><!-- validate/update/create --> <prop key="hibernate.show_sql">true</prop> <prop key="hibernate.format_sql">false</prop> <prop key="javax.persistence.validation.mode">none</prop> </props> </property> </bean> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <property name="defaultEncoding" value="utf-8"></property> </bean> <!-- 用於指定持久化實現廠商類 --> <bean id="persistenceProvider" class="org.hibernate.ejb.HibernatePersistence" /> <!-- 用於設置JPA實現廠商的特定屬性 --> <bean id="jpaVendorAdapter" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"> <property name="database" value="MYSQL" /> </bean> <!-- 用於指定一些高級特性 --> <bean id="jpaDialect" class="org.springframework.orm.jpa.vendor.HibernateJpaDialect" /> <!-- 事務管理器 --> <bean id="txManager" class="org.springframework.orm.jpa.JpaTransactionManager"> <property name="entityManagerFactory" ref="entityManagerFactory" /> </bean> <!-- 注解式事務 --> <tx:annotation-driven transaction-manager="txManager" /> <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <property name="realm" ref="shiroDbRealm" /> </bean> <!-- 項目自定義的Realm --> <bean id="shiroDbRealm" class="org.shiro.demo.service.realm.ShiroDbRealm" ></bean> <!-- Shiro Filter --> <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <property name="securityManager" ref="securityManager" /> <property name="loginUrl" value="/" /> <property name="successUrl" value="/system/main" /> <property name="unauthorizedUrl" value="/system/error" /> <property name="filterChainDefinitions"> <value> /login = anon /validateCode = anon /** = authc </value> </property> </bean> </beans>
spring-mvc.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd"> <mvc:annotation-driven /> <!-- 默認訪問跳轉到登錄頁面 --> <mvc:view-controller path="/" view-name="forward:/login" /> <context:component-scan base-package="org.shiro.demo.controller" /> <mvc:resources mapping="/resources/**" location="/resources/" /> <!-- 采用SpringMVC自帶的JSON轉換工具,支持@ResponseBody注解 --> <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"> <property name="messageConverters"> <list> <bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter" /> </list> </property> </bean> <!-- 避免IE執行AJAX時,返回JSON出現下載文件 --> <bean id="mappingJacksonHttpMessageConverter" class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"> <property name="supportedMediaTypes"> <list> <value>text/html;charset=UTF-8</value> </list> </property> </bean> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="viewClass" value="org.springframework.web.servlet.view.JstlView" /> <property name="prefix" value="/" /> <property name="suffix" value=".jsp" /> </bean> <!-- 開啟Shiro注解的Spring配置方式的beans。在lifecycleBeanPostProcessor之后運行 --> <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="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" /> <!-- shiro為集成spring --> <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"> <property name="exceptionMappings"> <props> <prop key="org.apache.shiro.authz.UnauthorizedException">/system/error</prop> </props> </property> </bean> </beans>
jdbc.properties
jdbc.driverClassName=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/shirodemo?useUnicode=true&characterEncoding=UTF-8 jdbc.username=root jdbc.password=root BoneCP.idleConnectionTestPeriod=60 BoneCP.idleMaxAge=60 BoneCP.maxConnectionsPerPartition=5 BoneCP.minConnectionsPerPartition=1 BoneCP.partitionCount=3 BoneCP.acquireIncrement=2 BoneCP.statementsCacheSize=0 BoneCP.releaseHelperThreads=3
log4j.properties
log4j.rootLogger=INFO,stdout,file
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout.ConversionPattern=%-d{yyyy-MM-dd HH\:mm\:ss,SSS} [%c]-[%p] %m%n
log4j.appender.file=org.apache.log4j.DailyRollingFileAppender
log4j.appender.file.File=${spring_springmvc_jpa.root}/shirodemo.log
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.R.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss,SSS} [%t] [%c] [%p] - %m%n
log4j.logger.org.hibernate.tool.hbm2ddl=info
(3)建立JavaBean,User.java,Role.java,Permisson.java
User.java
package org.shiro.demo.entity; import java.io.Serializable; import java.util.Collection; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.JoinTable; import javax.persistence.ManyToMany; import javax.persistence.Table; import org.codehaus.jackson.annotate.JsonIgnore; @Entity @Table(name = "CMSUser") public class User implements Serializable { private static final long serialVersionUID = 7419229779731522702L; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "userid") private Long id; @Column(length = 50, unique = true) private String account; @Column(length = 100) @JsonIgnore //springmvc生成json不包含此字段 private String password; @Column(length = 50) private String nickname; @ManyToMany(cascade = {CascadeType.PERSIST }) @JsonIgnore @JoinTable(name = "CMSUserRole", joinColumns = { @JoinColumn(name = "userid", referencedColumnName = "userid") }, inverseJoinColumns = { @JoinColumn(name = "roleid", referencedColumnName = "roleid") }) private Collection<Role> roles; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getAccount() { return account; } public void setAccount(String account) { this.account = account; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getNickname() { return nickname; } public void setNickname(String nickname) { this.nickname = nickname; } public Collection<Role> getRoles() { return roles; } public void setRoles(Collection<Role> roles) { this.roles = roles; } /** * 本函數輸出將作為默認的<shiro:principal/>輸出. */ public String toString() { return account; } }
Role.java
package org.shiro.demo.entity; import java.io.Serializable; import java.util.Collection; import javax.persistence.Basic; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.JoinTable; import javax.persistence.ManyToMany; import javax.persistence.Table; @Entity @Table(name = "CMSRole") public class Role implements Serializable{ private static final long serialVersionUID = 6177417450707400228L; @Id @GeneratedValue(strategy=GenerationType.IDENTITY) @Column(name="roleid") private Long id; @Column(length=50) private String name; @Column(length=50) private String description; @ManyToMany(mappedBy = "roles") @Basic(fetch = FetchType.LAZY) private Collection<User> users; @ManyToMany(cascade={CascadeType.PERSIST,CascadeType.REFRESH,CascadeType.MERGE}, fetch = FetchType.LAZY) @JoinTable(name = "CMSRolePms", joinColumns = { @JoinColumn(name = "roleid", updatable = false) }, inverseJoinColumns = { @JoinColumn(name = "pmsid", updatable = false) }) private Collection<Permission> pmss; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public Collection<User> getUsers() { return users; } public void setUsers(Collection<User> users) { this.users = users; } public Collection<Permission> getPmss() { return pmss; } public void setPmss(Collection<Permission> pmss) { this.pmss = pmss; } }
Permisson.java
package org.shiro.demo.entity; import java.io.Serializable; import java.util.Collection; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToMany; import javax.persistence.ManyToOne; import javax.persistence.OneToMany; import javax.persistence.Table; @Entity @Table(name="CMSPermission") public class Permission implements Serializable{ private static final long serialVersionUID = -8792590494605747957L; @Id @GeneratedValue(strategy=GenerationType.IDENTITY) @Column(name="pmsid") private Long id; @Column(length=50) private String name; @Column(length=100) private String description; @Column(length=50) private String permission; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "parentid") private Permission parent; @OneToMany(mappedBy = "parent",fetch = FetchType.LAZY,cascade={CascadeType.ALL}) private Collection<Permission> children; @ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "pmss") private Collection<Role> roles; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public String getPermission() { return permission; } public void setPermission(String permission) { this.permission = permission; } public Permission getParent() { return parent; } public void setParent(Permission parent) { this.parent = parent; } public Collection<Permission> getChildren() { return children; } public void setChildren(Collection<Permission> children) { this.children = children; } public Collection<Role> getRoles() { return roles; } public void setRoles(Collection<Role> roles) { this.roles = roles; } }
至此,配置基本完成,請繼續學習下一篇文章:
SpringMVC+Apache Shiro+JPA(hibernate)案例教學(二)基於SpringMVC+Shiro的用戶登錄權限驗證
SpringMVC+Apache Shiro+JPA(hibernate)案例教學(三)給Shiro登錄驗證加上驗證碼
SpringMVC+Apache Shiro+JPA(hibernate)案例教學(四)基於Shiro驗證用戶權限,且給用戶授權。