shiro教程(4)-shiro与项目集成开发
1?shiro與項(xiàng)目集成開發(fā)
這里我們主要以用戶登錄的例子來演示,先給出一個(gè)時(shí)序圖:
點(diǎn)擊打開鏈接(點(diǎn)擊查看)
1.1?shiro與spring web項(xiàng)目整合
shiro與springweb項(xiàng)目整合在“基于url攔截實(shí)現(xiàn)的工程”基礎(chǔ)上整合,基于url攔截實(shí)現(xiàn)的工程的技術(shù)架構(gòu)是springmvc+mybatis,整合注意兩點(diǎn):
1、shiro與spring整合
2、加入shiro對web應(yīng)用的支持
1.1.1?取消原springmvc認(rèn)證和授權(quán)攔截器
去掉springmvc.xml中配置的LoginInterceptor和PermissionInterceptor攔截器。
1.1.2?加入shiro的jar包
?
1.1.3?web.xml添加shiro Filter
<!-- shiro過慮器,DelegatingFilterProx會(huì)從spring容器中找shiroFilter --><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></filter-mapping>1.1.4?applicationContext-shiro.xml?
<!-- Shiro 的Web過濾器 --><bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"><property name="securityManager" ref="securityManager" /><!-- 如果沒有認(rèn)證將要跳轉(zhuǎn)的登陸地址,http可訪問的url,如果不在表單認(rèn)證過慮器FormAuthenticationFilter中指定此地址就為身份認(rèn)證地址 --><property name="loginUrl" value="/login.action" /><!-- 沒有權(quán)限跳轉(zhuǎn)的地址 --><property name="unauthorizedUrl" value="/refuse.jsp" /><!-- shiro攔截器配置 --><property name="filters"><map><entry key="authc" value-ref="formAuthenticationFilter" /></map></property><property name="filterChainDefinitions"><value><!-- 必須通過身份認(rèn)證方可訪問,身份認(rèn) 證的url必須和過慮器中指定的loginUrl一致 -->/loginsubmit.action = authc<!-- 退出攔截,請求logout.action執(zhí)行退出操作 -->/logout.action = logout<!-- 無權(quán)訪問頁面 -->/refuse.jsp = anon<!-- roles[XX]表示有XX角色才可訪問 -->/item/list.action = roles[item],authc/js/** anon/images/** anon/styles/** anon<!-- user表示身份認(rèn)證通過或通過記住我認(rèn)證通過的可以訪問 -->/** = user<!-- /**放在最下邊,如果一個(gè)url有多個(gè)過慮器則多個(gè)過慮器中間用逗號分隔,如:/** = user,roles[admin] --></value></property></bean><!-- 安全管理器 --><bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"><property name="realm" ref="userRealm" /></bean><!-- 自定義 realm --><bean id="userRealm" class="com.sihai.ssm.realm.CustomRealm1"></bean><!-- 基于Form表單的身份驗(yàn)證過濾器,不配置將也會(huì)注冊此過慮器,表單中的用戶賬號、密碼及l(fā)oginurl將采用默認(rèn)值,建議配置 --><bean id="formAuthenticationFilter"class="org.apache.shiro.web.filter.authc.FormAuthenticationFilter"><!-- 表單中賬號的input名稱 --><property name="usernameParam" value="usercode" /><!-- 表單中密碼的input名稱 --><property name="passwordParam" value="password" /><!-- <property name="rememberMeParam" value="rememberMe"/> --><!-- loginurl:用戶登陸地址,此地址是可以http訪問的url地址 --><property name="loginUrl" value="/loginsubmit.action" /></bean>securityManager:這個(gè)屬性是必須的。
loginUrl:沒有登錄認(rèn)證的用戶請求將跳轉(zhuǎn)到此地址,不是必須的屬性,不輸入地址的話會(huì)自動(dòng)尋找項(xiàng)目web項(xiàng)目的根目錄下的”/login.jsp”頁面。
unauthorizedUrl:沒有權(quán)限默認(rèn)跳轉(zhuǎn)的頁面。
1.1.5?使用shiro注解授權(quán)
在springmvc.xml中配置shiro注解支持,可在controller方法中使用shiro注解配置權(quán)限:
<!-- 開啟aop,對類代理 --><aop:config proxy-target-class="true"></aop:config><!-- 開啟shiro注解支持 --><beanclass="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor"><property name="securityManager" ref="securityManager" /></bean>修改Controller代碼,在方法上添加授權(quán)注解,如下:
// 查詢商品列表@RequestMapping("/queryItem")@RequiresPermissions("item:query")public ModelAndView queryItem() throws Exception {上邊代碼@RequiresPermissions("item:query")表示必須擁有“item:query”權(quán)限方可執(zhí)行。
其它的方法參考示例添加注解
1.1.6?自定義realm
此realm先不從數(shù)據(jù)庫查詢權(quán)限數(shù)據(jù),當(dāng)前需要先將shiro整合完成,在上邊章節(jié)定義的realm基礎(chǔ)上修改。
public class CustomRealm1 extends AuthorizingRealm {@Autowiredprivate SysService sysService;@Overridepublic String getName() {return "customRealm";}// 支持什么類型的token@Overridepublic boolean supports(AuthenticationToken token) {return token instanceof UsernamePasswordToken;}// 認(rèn)證@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {// 從token中 獲取用戶身份信息String username = (String) token.getPrincipal();// 拿username從數(shù)據(jù)庫中查詢// ....// 如果查詢不到則返回nullif (!username.equals("zhang")) {// 這里模擬查詢不到return null;}// 獲取從數(shù)據(jù)庫查詢出來的用戶密碼String password = "123";// 這里使用靜態(tài)數(shù)據(jù)模擬。。// 根據(jù)用戶id從數(shù)據(jù)庫取出菜單//...先用靜態(tài)數(shù)據(jù)List<SysPermission> menus = new ArrayList<SysPermission>();;SysPermission sysPermission_1 = new SysPermission();sysPermission_1.setName("商品管理");sysPermission_1.setUrl("/item/queryItem.action");SysPermission sysPermission_2 = new SysPermission();sysPermission_2.setName("用戶管理");sysPermission_2.setUrl("/user/query.action");menus.add(sysPermission_1);menus.add(sysPermission_2);// 構(gòu)建用戶身體份信息ActiveUser activeUser = new ActiveUser();activeUser.setUserid(username);activeUser.setUsername(username);activeUser.setUsercode(username);activeUser.setMenus(menus);// 返回認(rèn)證信息由父類AuthenticatingRealm進(jìn)行認(rèn)證SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(activeUser, password, getName());return simpleAuthenticationInfo;}// 授權(quán)@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {// 獲取身份信息ActiveUser activeUser = (ActiveUser) principals.getPrimaryPrincipal();//用戶idString userid = activeUser.getUserid();// 根據(jù)用戶id從數(shù)據(jù)庫中查詢權(quán)限數(shù)據(jù)// ....這里使用靜態(tài)數(shù)據(jù)模擬List<String> permissions = new ArrayList<String>();permissions.add("item:query");permissions.add("item:update");// 將權(quán)限信息封閉為AuthorizationInfoSimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();for (String permission : permissions) {simpleAuthorizationInfo.addStringPermission(permission);}return simpleAuthorizationInfo;}}1.1.7?登錄
//用戶登陸頁面@RequestMapping("/login")public String login()throws Exception{return "login";}// 用戶登陸提交@RequestMapping("/loginsubmit")public String loginsubmit(Model model, HttpServletRequest request)throws Exception {// shiro在認(rèn)證過程中出現(xiàn)錯(cuò)誤后將異常類路徑通過request返回String exceptionClassName = (String) request.getAttribute("shiroLoginFailure");if (UnknownAccountException.class.getName().equals(exceptionClassName)) {throw new CustomException("賬號不存在");} else if (IncorrectCredentialsException.class.getName().equals(exceptionClassName)) {throw new CustomException("用戶名/密碼錯(cuò)誤");} else{throw new Exception();//最終在異常處理器生成未知錯(cuò)誤}}1.1.8?首頁
由于session由shiro管理,需要修改首頁的controller方法:
//系統(tǒng)首頁@RequestMapping("/first")public String first(Model model)throws Exception{//主體Subject subject = SecurityUtils.getSubject();//身份ActiveUser activeUser = (ActiveUser) subject.getPrincipal();model.addAttribute("activeUser", activeUser);return "/first";}1.1.9?退出
由于使用shiro的sessionManager,不用開發(fā)退出功能,使用shiro的logout攔截器即可。
<!-- 退出攔截,請求logout.action執(zhí)行退出操作 -->/logout.action = logout1.1.10?無權(quán)限r(nóng)efuse.jsp
當(dāng)用戶無操作權(quán)限,shiro將跳轉(zhuǎn)到refuse.jsp頁面。
參考:applicationContext-shiro.xml
1.2?realm連接數(shù)據(jù)庫
1.2.1?添加憑證匹配器
添加憑證匹配器實(shí)現(xiàn)md5加密校驗(yàn)。
修改applicationContext-shiro.xml:
<!-- 憑證匹配器 --><bean id="credentialsMatcher"class="org.apache.shiro.authc.credential.HashedCredentialsMatcher"><property name="hashAlgorithmName" value="md5" /><property name="hashIterations" value="1" /></bean><!-- 自定義 realm --><bean id="userRealm" class="com.sihai.ssm.realm.CustomRealm1"><property name="credentialsMatcher" ref="credentialsMatcher" /></bean>1.2.2?realm代碼
修改realm代碼從數(shù)據(jù)庫中查詢用戶身份信息和權(quán)限信息,將sysService注入realm。
public class CustomRealm1 extends AuthorizingRealm {@Autowiredprivate SysService sysService;@Overridepublic String getName() {return "customRealm";}// 支持什么類型的token@Overridepublic boolean supports(AuthenticationToken token) {return token instanceof UsernamePasswordToken;}@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {// 從token中獲取用戶身份String usercode = (String) token.getPrincipal();SysUser sysUser = null;try {sysUser = sysService.findSysuserByUsercode(usercode);} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}// 如果賬號不存在if (sysUser == null) {throw new UnknownAccountException("賬號找不到");}// 根據(jù)用戶id取出菜單List<SysPermission> menus = null;try {menus = sysService.findMenuList(sysUser.getId());} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}// 用戶密碼String password = sysUser.getPassword();//鹽String salt = sysUser.getSalt();// 構(gòu)建用戶身體份信息ActiveUser activeUser = new ActiveUser();activeUser.setUserid(sysUser.getId());activeUser.setUsername(sysUser.getUsername());activeUser.setUsercode(sysUser.getUsercode());activeUser.setMenus(menus);SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(activeUser, password, ByteSource.Util.bytes(salt),getName());return simpleAuthenticationInfo;}@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {//身份信息ActiveUser activeUser = (ActiveUser) principals.getPrimaryPrincipal();//用戶idString userid = activeUser.getUserid();//獲取用戶權(quán)限List<SysPermission> permissions = null;try {permissions = sysService.findSysPermissionList(userid);} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}//構(gòu)建shiro授權(quán)信息SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();for(SysPermission sysPermission:permissions){simpleAuthorizationInfo.addStringPermission(sysPermission.getPercode());}return simpleAuthorizationInfo;}}1.3?緩存
shiro每個(gè)授權(quán)都會(huì)通過realm獲取權(quán)限信息,為了提高訪問速度需要添加緩存,第一次從realm中讀取權(quán)限數(shù)據(jù),之后不再讀取,這里Shiro和Ehcache整合。
1.3.1?添加Ehcache的jar包
1.3.2?配置
在applicationContext-shiro.xml中配置緩存管理器。
<!-- 安全管理器 --><bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"><property name="realm" ref="userRealm" /><property name="sessionManager" ref="sessionManager" /><property name="cacheManager" ref="cacheManager"/></bean><!-- 緩存管理器 --><bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager"></bean>1.4?session管理
在applicationContext-shiro.xml中配置sessionManager:
<!-- 安全管理器 --><bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"><property name="realm" ref="userRealm" /><property name="sessionManager" ref="sessionManager" /></bean><!-- 會(huì)話管理器 --><bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager"><!-- session的失效時(shí)長,單位毫秒 --><property name="globalSessionTimeout" value="600000"/><!-- 刪除失效的session --><property name="deleteInvalidSessions" value="true"/></bean>1.5?驗(yàn)證碼
1.5.1?自定義FormAuthenticationFilter
需要在驗(yàn)證賬號和名稱之前校驗(yàn)驗(yàn)證碼。
public class MyFormAuthenticationFilter extends FormAuthenticationFilter {protected boolean onAccessDenied(ServletRequest request,ServletResponse response, Object mappedValue) throws Exception {// 校驗(yàn)驗(yàn)證碼// 從session獲取正確的驗(yàn)證碼HttpSession session = ((HttpServletRequest)request).getSession();//頁面輸入的驗(yàn)證碼String randomcode = request.getParameter("randomcode");//從session中取出驗(yàn)證碼String validateCode = (String) session.getAttribute("validateCode");if (!randomcode.equals(validateCode)) {// randomCodeError表示驗(yàn)證碼錯(cuò)誤request.setAttribute("shiroLoginFailure", "randomCodeError");//拒絕訪問,不再校驗(yàn)賬號和密碼return true;}return super.onAccessDenied(request, response, mappedValue);}}1.5.2?修改FormAuthenticationFilter配置
修改applicationContext-shiro.xml中對FormAuthenticationFilter的配置。
<bean id="formAuthenticationFilter"class="org.apache.shiro.web.filter.authc.FormAuthenticationFilter">改為<bean id="formAuthenticationFilter"class="com.sihai.ssm.shiro.MyFormAuthenticationFilter">1.5.3?登陸頁面
添加驗(yàn)證碼:
<TR><TD>驗(yàn)證碼:</TD><TD><input id="randomcode" name="randomcode" size="8" /> <imgid="randomcode_img" src="${baseurl}validatecode.jsp" alt=""width="56" height="20" align='absMiddle' /> <ahref=javascript:randomcode_refresh()>刷新</a></TD></TR>1.5.4?配置validatecode.jsp匿名訪問
修改applicationContext-shiro.xml:
1.6?記住我
用戶登陸選擇“自動(dòng)登陸”本次登陸成功會(huì)向cookie寫身份信息,下次登陸從cookie中取出身份信息實(shí)現(xiàn)自動(dòng)登陸。
1.6.1?用戶身份實(shí)現(xiàn)java.io.Serializable接口
向cookie記錄身份信息需要用戶身份信息對象實(shí)現(xiàn)序列化接口,如下:
?
1.6.2?配置
<!-- 安全管理器 --><bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"><property name="realm" ref="userRealm" /><property name="sessionManager" ref="sessionManager" /><property name="cacheManager" ref="cacheManager"/><!-- 記住我 --><property name="rememberMeManager" ref="rememberMeManager"/></bean><!-- rememberMeManager管理器 --><bean id="rememberMeManager" class="org.apache.shiro.web.mgt.CookieRememberMeManager"><property name="cookie" ref="rememberMeCookie" /></bean><!-- 記住我cookie --><bean id="rememberMeCookie" class="org.apache.shiro.web.servlet.SimpleCookie"><constructor-arg value="rememberMe" /><!-- 記住我cookie生效時(shí)間30天 --><property name="maxAge" value="2592000" /></bean>修改formAuthenticationFitler添加頁面中“記住我checkbox”的input名稱:
<bean id="formAuthenticationFilter"class="com.sihai.ssm.shiro.MyFormAuthenticationFilter"><!-- 表單中賬號的input名稱 --><property name="usernameParam" value="usercode" /><!-- 表單中密碼的input名稱 --><property name="passwordParam" value="password" /><property name="rememberMeParam" value="rememberMe"/><!-- loginurl:用戶登陸地址,此地址是可以http訪問的url地址 --><property name="loginUrl" value="/loginsubmit.action" /></bean>1.6.3?登陸頁面
在login.jsp中添加“記住我”checkbox。
<TR><TD></TD><TD><input type="checkbox" name="rememberMe" />自動(dòng)登陸</TD></TR>2?附:
2.1?shiro過慮器
| 過濾器簡稱 | 對應(yīng)的java類 |
| anon | org.apache.shiro.web.filter.authc.AnonymousFilter |
| authc | org.apache.shiro.web.filter.authc.FormAuthenticationFilter |
| authcBasic | org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter |
| perms | org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter |
| port | org.apache.shiro.web.filter.authz.PortFilter |
| rest | org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter |
| roles | org.apache.shiro.web.filter.authz.RolesAuthorizationFilter |
| ssl | org.apache.shiro.web.filter.authz.SslFilter |
| user | org.apache.shiro.web.filter.authc.UserFilter |
| logout | org.apache.shiro.web.filter.authc.LogoutFilter |
anon:例子/admins/**=anon 沒有參數(shù),表示可以匿名使用。
authc:例如/admins/user/**=authc表示需要認(rèn)證(登錄)才能使用,沒有參數(shù)
roles:例子/admins/user/**=roles[admin],參數(shù)可以寫多個(gè),多個(gè)時(shí)必須加上引號,并且參數(shù)之間用逗號分割,當(dāng)有多個(gè)參數(shù)時(shí),例如admins/user/**=roles["admin,guest"],每個(gè)參數(shù)通過才算通過,相當(dāng)于hasAllRoles()方法。
perms:例子/admins/user/**=perms[user:add:*],參數(shù)可以寫多個(gè),多個(gè)時(shí)必須加上引號,并且參數(shù)之間用逗號分割,例如/admins/user/**=perms["user:add:*,user:modify:*"],當(dāng)有多個(gè)參數(shù)時(shí)必須每個(gè)參數(shù)都通過才通過,想當(dāng)于isPermitedAll()方法。
rest:例子/admins/user/**=rest[user],根據(jù)請求的方法,相當(dāng)于/admins/user/**=perms[user:method] ,其中method為post,get,delete等。
port:例子/admins/user/**=port[8081],當(dāng)請求的url的端口不是8081是跳轉(zhuǎn)到schemal://serverName:8081?queryString,其中schmal是協(xié)議http或https等,serverName是你訪問的host,8081是url配置里port的端口,queryString
是你訪問的url里的?后面的參數(shù)。
authcBasic:例如/admins/user/**=authcBasic沒有參數(shù)表示httpBasic認(rèn)證
ssl:例子/admins/user/**=ssl沒有參數(shù),表示安全的url請求,協(xié)議為https
user:例如/admins/user/**=user沒有參數(shù)表示必須存在用戶,當(dāng)?shù)侨氩僮鲿r(shí)不做檢查
注:
anon,authcBasic,auchc,user是認(rèn)證過濾器,
perms,roles,ssl,rest,port是授權(quán)過濾器
2.2?shiro的jsp標(biāo)簽
Jsp頁面添加:
<%@ tagliburi="http://shiro.apache.org/tags" prefix="shiro" %>
| 標(biāo)簽名稱 | 標(biāo)簽條件(均是顯示標(biāo)簽內(nèi)容) |
| <shiro:authenticated> | 登錄之后 |
| <shiro:notAuthenticated> | 不在登錄狀態(tài)時(shí) |
| <shiro:guest> | 用戶在沒有RememberMe時(shí) |
| <shiro:user> | 用戶在RememberMe時(shí) |
| <shiro:hasAnyRoles name="abc,123" > | 在有abc或者123角色時(shí) |
| <shiro:hasRole name="abc"> | 擁有角色abc |
| <shiro:lacksRole name="abc"> | 沒有角色abc |
| <shiro:hasPermission name="abc"> | 擁有權(quán)限資源abc |
| <shiro:lacksPermission name="abc"> | 沒有abc權(quán)限資源 |
| <shiro:principal> | 顯示用戶身份名稱 |
?<shiro:principal property="username"/>?????顯示用戶身份中的屬性值
總結(jié)
以上是生活随笔為你收集整理的shiro教程(4)-shiro与项目集成开发的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: shiro教程(3)-shiro授权
- 下一篇: 算法-Valid Anagram