spring shiro整合入门
?
?
spring和shiro的集成,需要额外加入的依赖:
?
<!-- shiro-web --><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-web</artifactId><version>${shiro-version}</version></dependency><!-- shiro-spring --><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-spring</artifactId><version>${shiro-version}</version></dependency><!-- shiro-ehcache --><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-ehcache</artifactId><version>${shiro-version}</version></dependency>
?
我使用的是struts2-hibernate-spring集成的shiro,全部的依赖关系:
?
<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/maven-v4_0_0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.tch.test</groupId><artifactId>template</artifactId><packaging>war</packaging><version>1.0.0</version><name>template Maven Webapp</name><url>http://maven.apache.org</url><dependencies><!-- junit --><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>${junit-version}</version><scope>test</scope></dependency><!-- spring-context --><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>${spring-version}</version></dependency><!-- spring-orm --><dependency><groupId>org.springframework</groupId><artifactId>spring-orm</artifactId><version>${spring-version}</version></dependency><!-- hibernate --><dependency><groupId>org.hibernate</groupId><artifactId>hibernate-core</artifactId><version>${hiberante-version}</version></dependency><!-- hibernate-proxool --><dependency><groupId>org.hibernate</groupId><artifactId>hibernate-proxool</artifactId><version>${hiberante-version}</version></dependency><!-- struts2 --><dependency><groupId>org.apache.struts</groupId><artifactId>struts2-core</artifactId><version>${struts2-version}</version></dependency><!-- struts2-spring-plugin --><dependency><groupId>org.apache.struts</groupId><artifactId>struts2-spring-plugin</artifactId><version>${struts2-version}</version></dependency><!-- mysql --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>${mysql-version}</version></dependency><!-- log4j --><dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>${log4j-version}</version></dependency><!-- slf4j-log4j --><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-log4j12</artifactId><version>${slf4j-version}</version></dependency><!-- aspectjrt --><dependency><groupId>org.aspectj</groupId><artifactId>aspectjrt</artifactId><version>${aspectj-version}</version></dependency><!-- aspectjweaver --><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>${aspectj-version}</version></dependency><!-- shiro-web --><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-web</artifactId><version>${shiro-version}</version></dependency><!-- shiro-spring --><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-spring</artifactId><version>${shiro-version}</version></dependency><!-- shiro-ehcache --><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-ehcache</artifactId><version>${shiro-version}</version></dependency></dependencies><build><finalName>template</finalName></build><properties><junit-version>4.11</junit-version><spring-version>3.2.6.RELEASE</spring-version><hiberante-version>3.6.10.Final</hiberante-version><struts2-version>2.3.16</struts2-version><mysql-version>5.1.28</mysql-version><log4j-version>1.2.17</log4j-version><slf4j-version>1.7.5</slf4j-version><aspectj-version>1.7.4</aspectj-version><shiro-version>1.2.2</shiro-version></properties></project>
?
?
下面开始配置:
?
首先是web.xml:
<?xml version="1.0" encoding="UTF-8"?><web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"><context-param><param-name>contextConfigLocation</param-name><param-value>classpath*:applicationContext*.xml</param-value></context-param><!-- 配置log4j --><!--说明:如果没有webAppRootKey这个设置,同一个tomcat下的多个项目要使用log4j就会导致WebApp.root冲突的错误--><context-param><param-name>webAppRootKey</param-name><param-value>mywebapp1.root</param-value></context-param><!-- 配置log4j.properties的位置 --><context-param><param-name>log4jConfigLocation</param-name><param-value>classpath:log4j.properties</param-value></context-param><!-- 配置spring扫描log4j配置文件(log4j.properties)的时间间隔,当修改了配置文件之后可以立即生效 --><context-param><param-name>log4jRefreshInterval</param-name><param-value>6000</param-value></context-param><!-- log4j listener配置到ContextLoaderListener之前 --><listener><listener-class>org.springframework.web.util.Log4jConfigListener</listener-class></listener><!-- spring监听器 --><listener><listener-class>org.springframework.web.context.ContextLoaderListener</listener-class></listener><!-- shiro security filter 要配置在struts2的filter前面,并且要求可以拦截struts2匹配的请求--><filter><filter-name>shiroFilter</filter-name><filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class><init-param><param-name>targetFilterLifecycle</param-name><param-value>true</param-value></init-param></filter><filter-mapping><filter-name>shiroFilter</filter-name><url-pattern>*.htm</url-pattern></filter-mapping><filter-mapping><filter-name>shiroFilter</filter-name><url-pattern>*.action</url-pattern></filter-mapping><!-- struts2 filter --><filter><filter-name>struts2</filter-name><filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class></filter><filter-mapping><filter-name>struts2</filter-name><url-pattern>*.htm</url-pattern></filter-mapping><filter-mapping><filter-name>struts2</filter-name><url-pattern>*.action</url-pattern></filter-mapping><welcome-file-list><welcome-file>index.jsp</welcome-file></welcome-file-list></web-app>
?
?
下面是log4j.properties的配置:(使用了mywebapp1.root)
log4j.rootLogger=info,DAILY_ROLLING_FILE,stdout#log4j.rootLogger=ERROR,CONSOLE,DAILY_ROLLING_FILElog4j.appender.stdout=org.apache.log4j.ConsoleAppenderlog4j.appender.stdout.layout=org.apache.log4j.PatternLayoutlog4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m%n######################### DailyRolling File########################log4j.appender.DAILY_ROLLING_FILE=org.apache.log4j.DailyRollingFileAppenderlog4j.appender.DAILY_ROLLING_FILE.Append=truelog4j.appender.DAILY_ROLLING_FILE.Threshold=debuglog4j.appender.DAILY_ROLLING_FILE.Encoding=UTF-8###\u65e5\u5fd7\u76ee\u5f55\u6587\u4ef6log4j.appender.DAILY_ROLLING_FILE.File=${mywebapp1.root}/log.txtlog4j.appender.DAILY_ROLLING_FILE.DatePattern='.'yyyy-MM-ddlog4j.appender.DAILY_ROLLING_FILE.layout=org.apache.log4j.PatternLayoutlog4j.appender.DAILY_ROLLING_FILE.layout.ConversionPattern=[%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} [%c] %m%n#################### Console Appender###################log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppenderlog4j.appender.Threshold=debuglog4j.appender.CONSOLE.Target=System.outlog4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayoutlog4j.appender.CONSOLE.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %5p (%c:%L) - %m%n
?
?
?
applicationContext.xml的配置:
<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"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsdhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsdhttp://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd"><!-- 这个配置使用了hibernate.cfg.xml --><!-- 启动对@Aspectj注解的支持(自定义切面)--><aop:aspectj-autoproxy proxy-target-value="com.mysql.jdbc.Driver"></property><property name="url" value="jdbc:mysql://localhost:3306/test"></property><property name="username" value="root"></property><property name="password" value="root"></property></bean> --><!-- 事务管理器 --><bean id="transactionManager"ref="sessionFactory" /></bean><!--读取数据库配置文件 --><bean id="sessionFactory"ref="dataSource"/>--><property name="configLocation"> <value>classpath:hibernate.cfg.xml</value> </property><!-- 配置xml映射实体类 --><property name="mappingLocations"> <list> <!-- <value>classpath*:com/tch/test/template/po/*.hbm.xml</value> --> </list></property><!-- 配置注解映射实体类的包名 --><property name="packagesToScan"> <list> <value>com.tch.test.template.po.annotation</value> </list></property><property name="hibernateProperties"><props><!-- 输出hibernate的sql语句 --><prop key="hibernate.show_sql">true</prop><!-- 自动建表 --><prop key="hibernate.hbm2ddl.auto">update</prop></props></property></bean><!-- <bean id="sessionFactory"ref="dataSource"/><property name="mappingLocations"> <value>classpath:com/tch/test/ssh/entity/*.hbm.xml</value></property><property name="hibernateProperties"><props><prop key="hibernate.show_sql">true</prop></props></property></bean>--></beans>
?
?
applicationContext-shiro.xml 的配置:(spring中关于shiro的配置单独出来)
<?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:util="http://www.springframework.org/schema/util" 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"> <bean ref="cacheManager"/> <property name="realm" ref="DefaultRealm"/> </bean> <bean id="cacheManager" ref="securityManager"/> <property name="loginUrl" value="/user/pages/index.jsp"/> <property name="successUrl" value="/user/pages/success.jsp"/> <property name="unauthorizedUrl" value="/user/pages/error.jsp"/> <!-- 为什么不配置filter的时候,不能处理authc的请求(那些需要授权的请求不是应该子安转到unauthorizedUrl的地址吗?非要配置authc这个filter才能控制跳转到loginUrl) --> <property name="filters"> <util:map> <!-- 配置这个filter是为了让那些还没有登录的请求跳转到loginUrl去进行登录 --> <entry key="authc"> <bean name="code"><?xml version='1.0' encoding='UTF-8'?><!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"><hibernate-configuration><session-factory><property name="connection.provider_class">org.hibernate.connection.ProxoolConnectionProvider</property><property name="hibernate.proxool.pool_alias">mydb_pool</property><property name="hibernate.proxool.xml">proxool.xml</property><property name="dialect">com.tch.test.template.common.util.hibernate.MMySQLDialect</property></session-factory></hibernate-configuration>
?
proxool.xml:
<?xml version="1.0" encoding="UTF-8"?><something-else-entirely> <proxool> <alias>mydb_pool</alias> <driver-url>jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8</driver-url> <driver-class>com.mysql.jdbc.Driver</driver-class> <driver-properties> <property name="user" value="root"/> <property name="password" value="root"/> </driver-properties> <house-keeping-sleep-time>90000</house-keeping-sleep-time> <house-keeping-test-sql>select CURRENT_DATE</house-keeping-test-sql> <prototype-count>4</prototype-count> <simultaneous-build-throttle>50</simultaneous-build-throttle> <maximum-connection-count>300</maximum-connection-count> <minimum-connection-count>2</minimum-connection-count> <maximum-connection-lifetime>3600000</maximum-connection-lifetime> </proxool></something-else-entirely>
?
?
struts.xml配置:
<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE struts PUBLIC"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN""http://struts.apache.org/dtds/struts-2.3.dtd"><struts><package name="default" namespace="/" extends="struts-default"><default-action-ref name="login" /><global-results><result name="error">/error.jsp</result></global-results><global-exception-mappings><exception-mapping exception="java.lang.Exception"result="error" /></global-exception-mappings><action name="doLogin" method="doLogin"><result>/user/pages/success.jsp</result><result name="error">/user/pages/error.jsp</result></action><action name="authorize" method="authorize"><result>/user/pages/haspermission.jsp</result><result name="nopermission">/user/pages/nopermission.jsp</result></action><action name="logout" method="logout"><result>/user/pages/logout.jsp</result></action><action name="login"><result>/user/pages/index.jsp</result></action></package></struts>
?
?
struts.properties:
#国际化字符编码struts.i18n.encoding=UTF-8#处理请求的后缀格式struts.action.extension=htm,action#开发模式struts.devMode=true#是否允许动态方法调用struts.enable.DynamicMethodInvocation=true#限制最大文件上传大小struts.multipart.maxSize=10485760
?
?
自定义的Realm:
/** * */package com.tch.test.template.common.shiro;import java.util.HashSet;import java.util.List;import javax.annotation.Resource;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.subject.PrincipalCollection;import org.springframework.stereotype.Component;import com.tch.test.template.common.util.CollectionUtils;import com.tch.test.template.dao.IUserDao;import com.tch.test.template.po.annotation.Permission;import com.tch.test.template.po.annotation.Role;import com.tch.test.template.po.annotation.User;/** * 登录和检查授权的时候调用当前类 */@Component("DefaultRealm")public class DefaultRealm extends AuthorizingRealm {@Resource(name="userDao")private IUserDao userDao;public DefaultRealm() {setName("DefaultRealm"); // This name must match the name in the User}// 登录时shiro会用到的方法protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException {UsernamePasswordToken token = (UsernamePasswordToken) authcToken;// 通过表单接收的用户名String username = token.getUsername();if (username != null && !"".equals(username)) {User user = userDao.getUserByUsername(username);//第二个参数是从数据库中获取到的用户密码(或者密码的MD5),交给shiro去进行校验return new SimpleAuthenticationInfo(username, user.getPassword(),getName());}return null;}// 权限验证protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {try {String username = (String) principals.fromRealm(getName()).iterator().next();if (username != null) {// 查询用户授权信息SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();List<Role> roles = userDao.getRoles(username);//取出集合元素对象的roleName属性集合List<String> roleNames = CollectionUtils.getPropertyList(roles, Role.class, "roleName");List<Permission> permissions = userDao.getPermissions(username);//取出集合元素对象的permissionName属性集合List<String> permissionNames = CollectionUtils.getPropertyList(permissions, Permission.class, "permissionName");info.setRoles(new HashSet<String>(roleNames));info.setStringPermissions(new HashSet<String>(permissionNames));return info;}} catch (Exception e) {e.printStackTrace();}return null;}}
?
?
action:
package com.tch.test.template.web.action;import org.apache.log4j.Logger;import org.apache.shiro.SecurityUtils;import org.apache.shiro.authc.UsernamePasswordToken;import org.apache.shiro.subject.Subject;import org.apache.struts2.ServletActionContext;import org.springframework.context.annotation.Scope;import org.springframework.stereotype.Component;import com.opensymphony.xwork2.ActionSupport;@Component("userAction")@Scope("prototype")public class UserAction extends ActionSupport {private static final long serialVersionUID = 1L;private Logger log = Logger.getLogger(UserAction.class);private String username;private String password;public String doLogin(){try {UsernamePasswordToken token = new UsernamePasswordToken(username, password);/** * 设置setRememberMe为true的话,当用户登录失败之后,用户名仍旧会被记录在当前session中,登录成功的话,无论setRememberMe为true/false都会记录在session中 * 设置为false的话,登录失败就不会将用户名信息记录到session中了,登录成功才会保存到session中 *///token.setRememberMe(true);Subject subject = SecurityUtils.getSubject();boolean isAuthenticated = false;try{//登录操作(会调用DefaultRealm的doGetAuthenticationInfo进行验证,验证不通过会抛出异常)subject.login(token);//判断是否验证通过isAuthenticated = subject.isAuthenticated();}catch(Exception e){log.error("doLogin异常:", e);return ERROR;}System.out.println("subject.isAuthenticated() : "+isAuthenticated);if(isAuthenticated){System.out.println(" authenticated .....");}System.out.println("doLogin........");} catch (Exception e) {e.printStackTrace();}return SUCCESS;}public String authorize(){try{Subject subject = SecurityUtils.getSubject();//检查是否有permission1的权限(调用DefaultRealm的doGetAuthorizationInfo方法检查授权)boolean isPermitted = subject.isPermitted("permission1");if(! isPermitted){//没有权限return "nopermission";}}catch(Exception e){log.error("authorize异常:", e);e.printStackTrace();}return SUCCESS;}public String logout(){Subject subject = SecurityUtils.getSubject();try{//退出登录(移除当前用户的信息)subject.logout();javax.servlet.http.HttpSession session = ServletActionContext.getRequest().getSession();session.invalidate();System.out.println("logout ...");}catch(Exception e){log.error("logout异常:", e);e.printStackTrace();}return SUCCESS;}public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}}
?
?
?
?
index.jsp:
<%@ page language="java" pageEncoding="UTF-8"%><% String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";%><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html><head> <title></title> <script type="text/javascript" src="<%=basePath%>common/js/jquery-1.4.2.min.js"></script> <script type="text/javascript"> var basepath = '<%=basePath%>'; $(function(){ $("#username").focus();; }); $(function(){ $("#myform").attr('action','<%=basePath%>'+'doLogin.htm'); }); function tijiao(){ $("#myform").get(0).submit(); } $(document).keyup(function(e){ if(e.keyCode==13){ tijiao(); } }); </script></head><body><form id="myform" method="post">用户名:<input type="text" name="username" id="username"><br>密 码:<input type="text" name="password" id="password"><br> <input type="button" value="提交" onclick="tijiao();"></form></body></html>
?
?
?
success.jsp:
<%@ page language="java" pageEncoding="UTF-8"%><% String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";%><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html><head> <title></title> <script type="text/javascript" src="<%=basePath%>common/js/jquery-1.4.2.min.js"></script> <script type="text/javascript"> var basepath = '<%=basePath%>'; function authorize(){ window.location.href=basepath+'authorize.htm'; } function logout(){ window.location.href=basepath+'logout.htm'; } </script></head><body><br>登陆成功!<br><a href="javascript:void(0)" onclick="authorize()">进入秘密页面</a><br><a href="javascript:void(0)" onclick="logout()">退出</a></body></html>
?
?
其余的页面放在附件里面了
?
现在数据库插入数据: user ? role ?permission ?user_role ? role_permission ?表
?
首先访问:(shiro是我的项目名称)
?
?
?
http://localhost:8080/shiro/index.htm ? 登录
?
登录成功之后,点击进入秘密页面 ?,会检查是否有权限(分别进入不同页面)
?
然后可以退出。。。
?
?
?
?
?
?
?
?
?