書接上回 構建 struts2 spring3 mybatis 的maven項目 構建 pom.xml
繼續在原有框架下 融合shiro ,具體shiro是啥 這裡就不解釋了,恩 反正功能挺強大的
本著先會用再深入的原則,還是嘗試著將shiro融入框架中
0 首先上下這個項目的整體結構圖
1 在導入shiro的jar包 在pom.xml中添加shiro的配置
... <shiro.version>1.2.1</shiro.version> ... <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>${shiro.version}</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-web</artifactId> <version>${shiro.version}</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-ehcache</artifactId> <version>${shiro.version}</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>${shiro.version}</version> </dependency> </dependencies>
2 在web.xml中導入 shiro的過濾器
<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>/*</url-pattern> <dispatcher>REQUEST</dispatcher> <dispatcher>FORWARD</dispatcher> </filter-mapping>
這裡要說明一下
shiro的過濾器是前置過濾器,需要添加在struts2的前面,如果放在struts2之後會報錯
然後是在spring的過濾配置中添加spring-shiro的配置文件
<listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <context-param> <param-name>contextConfigLocation</param-name> <param-value> classpath:applicationContext-*.xml </param-value> </context-param>
配置文件為applicationContext-shiro.xml, 因為這裡用了通配符 所以不用修改
3 然後 添加spring-shiro的配置文件
<?xml version="1.0" encoding="UTF-8"?> <beans...> <description>Shiro安全配置 來源於: http://shiro.apache.org/spring.html </description> <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <property name="realm" ref="shiroDbRealm" /> </bean> <bean id="shiroDbRealm" class="lqb.shiro.ShiroDbRealm" /> <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <property name="securityManager" ref="securityManager" /> <property name="loginUrl" value="/login_loginPage.do" /> <!-- 沒有權限或者失敗後跳轉的頁面 --> <property name="successUrl" value="/login_home.do" /> <property name="unauthorizedUrl" value="/other_error.do"/> <property name="filterChainDefinitions"> <value> /login_loginPage.do = anon /login_login.do = anon /login_home.do=authc /login_hello.do=authc /t1/**=roles[aa],perms[aaa] /t2/**=roles[bb],perms[baaa] /t3/**=roles[dd] </value> </property> </bean> <!-- 保證實現了Shiro內部lifecycle函數的bean執行 --> <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/> <!-- AOP式方法級權限檢查 --> <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor"> <property name="proxyTargetClass" value="true" /> </bean> <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor"> <property name="securityManager" ref="securityManager"/> </bean> </beans>
這裡需要就是下 filterChainDefinitions 的value
key 是 對應的跳轉路徑 這裡都是指定的struts2的跳轉 可以匹配通配符 *
value 是對應的過濾權限
anon 不需要驗證
authc 需要登錄驗證
roles[aa] 角色驗證 中括號內為指定的角色
perms[aaa] 權限驗證 中括號內衛指定的權限
4 添加shiro的緩存配置文件
<ehcache> <diskStore path="java.io.tmpdir/shiro-spring-sample"/> <defaultCache maxElementsInMemory="10000" eternal="false" timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="false" diskPersistent="false" diskExpiryThreadIntervalSeconds="120" /> <cache name="shiro-activeSessionCache" maxElementsInMemory="10000" eternal="true" overflowToDisk="true" diskPersistent="true" diskExpiryThreadIntervalSeconds="600"/> <cache name="org.apache.shiro.realm.SimpleAccountRealm.authorization" maxElementsInMemory="100" eternal="false" timeToLiveSeconds="600" overflowToDisk="false"/> </ehcache> View Code
5 角色 權限 實現
因為只是一個demo 所以就沒有弄角色表和權限表 只是模擬了一下 用戶--角色--權限 的5表結構
用戶是查的表 角色和權限只是假實現
6 修改 struts.xml
<struts> <!-- 全局包設置 --> <package name="defalutGlobal" namespace="/" extends="json-default"> </package> <!-- 自定義開發包 --> <package name="myDefault" extends="defalutGlobal"> <!--登錄Action --> <action name="login_*" class="loginAction" method="{1}" > <result name="loginPage">WEB-INF/pages/login.html</result> <result name="loginPageForm">WEB-INF/pages/login2.html</result> <result name="home">WEB-INF/pages/home.html</result> <result name="hello">WEB-INF/pages/hello.html</result> <result name="success" type="json"> <param name="root">jsonResult</param> </result> </action> <action name="other_*" class="otherAction" method="{1}"> <result name="error">WEB-INF/pages/other/error.html</result> </action> </package> <package name="t1" extends="defalutGlobal" namespace="/t1"> <action name="t1_*" class="test1Action" method="{1}"> <result name="t1">/WEB-INF/pages/t1/t1.html</result> <result name="t2">/WEB-INF/pages/t1/t2.html</result> <result name="t3">/WEB-INF/pages/t1/t3.html</result> <result name="toT2" type="redirect" >/t2/t2_t2.do</result> </action> </package> <package name="t2" extends="defalutGlobal" namespace="/t2"> <action name="t2_*" class="test2Action" method="{1}"> <result name="t1">/WEB-INF/pages/t2/t1.html</result> <result name="t2">/WEB-INF/pages/t2/t2.html</result> <result name="t3">/WEB-INF/pages/t2/t3.html</result> </action> </package> <package name="t3" extends="defalutGlobal" namespace="/t3"> <action name="t3_*" class="test3Action" method="{1}"> <result name="t1">/WEB-INF/pages/t3/t1.html</result> <result name="t2">/WEB-INF/pages/t3/t2.html</result> <result name="t3">/WEB-INF/pages/t3/t3.html</result> </action> </package> </struts>
這裡為了更好地測試shiro的權限角色控制 所以把 t1,t2,t3加了namespace
7 添加html
這裡就沒啥說的了 給個縮略圖吧
8 實現reaml
public class ShiroDbRealm extends AuthorizingRealm { @Autowired private UserService userService; @Autowired private RoleService roleService; @Autowired private PermissionService permissionService; /** * 認證回調函數,登錄時調用. */ @Override protected AuthenticationInfo doGetAuthenticationInfo( AuthenticationToken authcToken) throws AuthenticationException { UsernamePasswordToken token = (UsernamePasswordToken) authcToken; User user = userService.getByUserName(token.getUsername()); if (user != null) { return new SimpleAuthenticationInfo(new ShiroUser(user.getUsername(), user.getNickname()), user.getPassword(),getName()); } else { return null; } } /** * 授權查詢回調函數, 進行鑒權但緩存中無用戶的授權信息時調用. */ @Override protected AuthorizationInfo doGetAuthorizationInfo( PrincipalCollection principals) { ShiroUser shiroUser = (ShiroUser) principals.getPrimaryPrincipal(); SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); // 加載用戶的roles List<Role> roles = roleService.getByUserName(shiroUser.username); List<String> stringRoles = new ArrayList<String>(roles.size()); for (Role role : roles) { stringRoles.add(role.getRolename()); } info.addRoles(stringRoles); // 加載用戶的permissions List<Permission> permissions = permissionService.getByUserName(shiroUser.username); Set<String> stringPermissions = new HashSet<String>(permissions.size()); for (Permission permission : permissions) { stringPermissions.add(permission.getPermissionname()); } info.setStringPermissions(stringPermissions); return info; } /** * 自定義Authentication對象,使得Subject除了攜帶用戶的登錄名外還可以攜帶更多信息. */ public static class ShiroUser implements Serializable { private static final long serialVersionUID = -1373760761780840081L; private String username; private String nickname; public ShiroUser(String username, String nickname) { this.username = username; this.nickname = nickname; } /**------getset略--------*/ } }
9 action
public class LoginAction extends BaseAction{ private String username; private String password; @Autowired UserService userService; /** * 登錄頁面 */ public String loginPage(){return "loginPage"; } /** * home頁面 */ public String home(){ return "home"; } /** * hello頁面 */ public String hello(){ System.out.println(SecurityUtils.getSubject().hasRole("cc")); return "hello"; } /** * 登錄 */ public String login(){ Map<String,Object> map = new HashMap<String,Object>(); User u=new User(getUsername(),getPassword()); u=userService.check(u); if("0".equals(u.getRes())){ map.put("res", "true"); AuthenticationToken token = new UsernamePasswordToken(username,password);// username和password是從表單提交過來的 Subject currentUser = SecurityUtils.getSubject(); currentUser.login(token); }else{ map.put("res", "false"); } JSONObject json = JSONObject.fromObject(map);//將map對象轉換成json類型數據 setJsonResult(json.toString());//給result賦值,傳遞給頁面 return "success"; } /** * 登錄頁面 */ public String loginPageForm(){ String result="loginPageForm"; return result; } /** * 登錄 */ public String loginForm(){ System.out.println("loginForm"); String result="loginPageForm"; User u=new User(getUsername(),getPassword()); u=userService.check(u); if("0".equals(u.getRes())){ AuthenticationToken token = new UsernamePasswordToken(username,password);// username和password是從表單提交過來的 Subject currentUser = SecurityUtils.getSubject(); currentUser.login(token); result="home"; } return result; } /** * 登出 */ public String logout(){ Subject currentUser = SecurityUtils.getSubject();if (currentUser.isAuthenticated()) { currentUser.logout(); // session 會銷毀,在SessionListener監聽session銷毀,清理權限緩存 if (LOG.isDebugEnabled()) { LOG.debug("用戶" + username + "退出登錄"); } }return "loginPage"; }
/**--------getset略----------*/
}
這裡就只上LoginAction了 其他的action 只是實現的跳轉沒有啥實際操作 就略過了
如果先看其他action 就只能下源碼了
這裡還要說一下 登錄頁面寫了兩個 一個是ajax的一個是form 沒有啥特別的 只是為了之後學習shiro remberme功能 打個提前量
10 數據庫結果
create table base_user ( id int not null auto_increment, createtime char(20), username char(20), password char(20), nickname char(20), t1 char(100), t2 char(30), primary key (id) );
好 完成 測試下 成功
最後總結下
1 不得不吐槽下 網上的關於shiro的教程雖然不算少 但大都是 springMVC的 關於struts2的還是比較少的
2 文檔還是官方的好 放一個中文的shiro參考手冊 下載
3 本人研究shiro時間不長 這裡只是作為一個入門參考 如果文中有錯誤的地方 盡情支出 歡迎技術噴子
4 下次打算把shiro的 rememberme等功能研究下 再寫一篇
5 本項目 下載