您好,登錄后才能下訂單哦!
shiro與springweb項目整合在“基于url攔截實現的工程”基礎上整合,基于url攔截實現的工程的技術架構是springmvc+mybatis,整合注意兩點:
1、shiro與spring整合
2、加入shiro對web應用的支持
去掉springmvc.xml中配置的LoginInterceptor和PermissionInterceptor攔截器。
[html] view plain copy print?
<!-- shiro過慮器,DelegatingFilterProx會從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>
[html] view plain copy print?
<!-- Shiro 的Web過濾器 -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager" />
<!-- 如果沒有認證將要跳轉的登陸地址,http可訪問的url,如果不在表單認證過慮器FormAuthenticationFilter中指定此地址就為身份認證地址 -->
<property name="loginUrl" value="/login.action" />
<!-- 沒有權限跳轉的地址 -->
<property name="unauthorizedUrl" value="/refuse.jsp" />
<!-- shiro攔截器配置 -->
<property name="filters">
<map>
<entry key="authc" value-ref="formAuthenticationFilter" />
</map>
</property>
<property name="filterChainDefinitions">
<value>
<!-- 必須通過身份認證方可訪問,身份認 證的url必須和過慮器中指定的loginUrl一致 -->
/loginsubmit.action = authc
<!-- 退出攔截,請求logout.action執行退出操作 -->
/logout.action = logout
<!-- 無權訪問頁面 -->
/refuse.jsp = anon
<!-- roles[XX]表示有XX角色才可訪問 -->
/item/list.action = roles[item],authc
/js/** anon
/p_w_picpaths/** anon
/styles/** anon
<!-- user表示身份認證通過或通過記住我認證通過的可以訪問 -->
/** = user
<!-- /**放在最下邊,如果一個url有多個過慮器則多個過慮器中間用逗號分隔,如:/** = 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="cn.itcast.ssm.realm.CustomRealm1">
</bean>
<!-- 基于Form表單的身份驗證過濾器,不配置將也會注冊此過慮器,表單中的用戶賬號、密碼及loginurl將采用默認值,建議配置 -->
<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:這個屬性是必須的。
loginUrl:沒有登錄認證的用戶請求將跳轉到此地址,不是必須的屬性,不輸入地址的話會自動尋找項目web項目的根目錄下的”/login.jsp”頁面。
unauthorizedUrl:沒有權限默認跳轉的頁面。
在springmvc.xml中配置shiro注解支持,可在controller方法中使用shiro注解配置權限:
[html] view plain copy print?
<!-- 開啟aop,對類代理 -->
<aop:config proxy-target-class="true"></aop:config>
<!-- 開啟shiro注解支持 -->
<bean
class="
org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager" />
</bean>
修改Controller代碼,在方法上添加授權注解,如下:
[java] view plain copy print?
// 查詢商品列表
@RequestMapping("/queryItem")
@RequiresPermissions("item:query")
public ModelAndView queryItem() throws Exception {
上邊代碼@RequiresPermissions("item:query")表示必須擁有“item:query”權限方可執行。
其它的方法參考示例添加注解
此realm先不從數據庫查詢權限數據,當前需要先將shiro整合完成,在上邊章節定義的realm基礎上修改。
[java] view plain copy print?
public class CustomRealm1 extends AuthorizingRealm {
@Autowired
private SysService sysService;
@Override
public String getName() {
return "customRealm";
}
// 支持什么類型的token
@Override
public boolean supports(AuthenticationToken token) {
return token instanceof UsernamePasswordToken;
}
// 認證
@Override
protected AuthenticationInfo doGetAuthenticationInfo(
AuthenticationToken token) throws AuthenticationException {
// 從token中 獲取用戶身份信息
String username = (String) token.getPrincipal();
// 拿username從數據庫中查詢
// ....
// 如果查詢不到則返回null
if (!username.equals("zhang")) {// 這里模擬查詢不到
return null;
}
// 獲取從數據庫查詢出來的用戶密碼
String password = "123";// 這里使用靜態數據模擬。。
// 根據用戶id從數據庫取出菜單
//...先用靜態數據
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);
// 構建用戶身體份信息
ActiveUser activeUser = new ActiveUser();
activeUser.setUserid(username);
activeUser.setUsername(username);
activeUser.setUsercode(username);
activeUser.setMenus(menus);
// 返回認證信息由父類AuthenticatingRealm進行認證
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(
activeUser, password, getName());
return simpleAuthenticationInfo;
}
// 授權
@Override
protected AuthorizationInfo doGetAuthorizationInfo(
PrincipalCollection principals) {
// 獲取身份信息
ActiveUser activeUser = (ActiveUser) principals.getPrimaryPrincipal();
//用戶id
String userid = activeUser.getUserid();
// 根據用戶id從數據庫中查詢權限數據
// ....這里使用靜態數據模擬
List<String> permissions = new ArrayList<String>();
permissions.add("item:query");
permissions.add("item:update");
// 將權限信息封閉為AuthorizationInfo
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
for (String permission : permissions) {
simpleAuthorizationInfo.addStringPermission(permission);
}
return simpleAuthorizationInfo;
}
}
[java] view plain copy print?
//用戶登陸頁面
@RequestMapping("/login")
public String login()throws Exception{
return "login";
}
// 用戶登陸提交
@RequestMapping("/loginsubmit")
public String loginsubmit(Model model, HttpServletRequest request)
throws Exception {
// shiro在認證過程中出現錯誤后將異常類路徑通過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("用戶名/密碼錯誤");
} else{
throw new Exception();//最終在異常處理器生成未知錯誤
}
}
由于session由shiro管理,需要修改首頁的controller方法:
[java] view plain copy print?
//系統首頁
@RequestMapping("/first")
public String first(Model model)throws Exception{
//主體
Subject subject = SecurityUtils.getSubject();
//身份
ActiveUser activeUser = (ActiveUser) subject.getPrincipal();
model.addAttribute("activeUser", activeUser);
return "/first";
}
由于使用shiro的sessionManager,不用開發退出功能,使用shiro的logout攔截器即可。
[html] view plain copy print?
<!-- 退出攔截,請求logout.action執行退出操作 -->
/logout.action = logout
當用戶無操作權限,shiro將跳轉到refuse.jsp頁面。
參考:applicationContext-shiro.xml
添加憑證匹配器實現md5加密校驗。
修改applicationContext-shiro.xml:
[html] view plain copy print?
<!-- 憑證匹配器 -->
<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="cn.itcast.ssm.realm.CustomRealm1">
<property name="credentialsMatcher" ref="credentialsMatcher" />
</bean>
修改realm代碼從數據庫中查詢用戶身份信息和權限信息,將sysService注入realm。
[java] view plain copy print?
public class CustomRealm1 extends AuthorizingRealm {
@Autowired
private SysService sysService;
@Override
public String getName() {
return "customRealm";
}
// 支持什么類型的token
@Override
public boolean supports(AuthenticationToken token) {
return token instanceof UsernamePasswordToken;
}
@Override
protected 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 block
e.printStackTrace();
}
// 如果賬號不存在
if (sysUser == null) {
throw new UnknownAccountException("賬號找不到");
}
// 根據用戶id取出菜單
List<SysPermission> menus = null;
try {
menus = sysService.findMenuList(sysUser.getId());
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// 用戶密碼
String password = sysUser.getPassword();
//鹽
String salt = sysUser.getSalt();
// 構建用戶身體份信息
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;
}
@Override
protected AuthorizationInfo doGetAuthorizationInfo(
PrincipalCollection principals) {
//身份信息
ActiveUser activeUser = (ActiveUser) principals.getPrimaryPrincipal();
//用戶id
String userid = activeUser.getUserid();
//獲取用戶權限
List<SysPermission> permissions = null;
try {
permissions = sysService.findSysPermissionList(userid);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//構建shiro授權信息
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
for(SysPermission sysPermission:permissions){
simpleAuthorizationInfo.addStringPermission(sysPermission.getPercode());
}
return simpleAuthorizationInfo;
}
}
shiro每個授權都會通過realm獲取權限信息,為了提高訪問速度需要添加緩存,第一次從realm中讀取權限數據,之后不再讀取,這里Shiro和Ehcache整合。
在applicationContext-shiro.xml中配置緩存管理器。
[html] view plain copy print?
<!-- 安全管理器 -->
<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>
在applicationContext-shiro.xml中配置sessionManager:
[html] view plain copy print?
<!-- 安全管理器 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="userRealm" />
<property name="sessionManager" ref="sessionManager" />
</bean>
<!-- 會話管理器 -->
<bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
<!-- session的失效時長,單位毫秒 -->
<property name="globalSessionTimeout" value="600000"/>
<!-- 刪除失效的session -->
<property name="deleteInvalidSessions" value="true"/>
</bean>
需要在驗證賬號和名稱之前校驗驗證碼。
[java] view plain copy print?
public class MyFormAuthenticationFilter extends FormAuthenticationFilter {
protected boolean onAccessDenied(ServletRequest request,
ServletResponse response, Object mappedValue) throws Exception {
// 校驗驗證碼
// 從session獲取正確的驗證碼
HttpSession session = ((HttpServletRequest)request).getSession();
//頁面輸入的驗證碼
String randomcode = request.getParameter("randomcode");
//從session中取出驗證碼
String validateCode = (String) session.getAttribute("validateCode");
if (!randomcode.equals(validateCode)) {
// randomCodeError表示驗證碼錯誤
request.setAttribute("shiroLoginFailure", "randomCodeError");
//拒絕訪問,不再校驗賬號和密碼
return true;
}
return super.onAccessDenied(request, response, mappedValue);
}
}
修改applicationContext-shiro.xml中對FormAuthenticationFilter的配置。
[html] view plain copy print?
<bean id="formAuthenticationFilter"
class="org.apache.shiro.web.filter.authc.FormAuthenticationFilter">
改為
<bean id="formAuthenticationFilter"
class="cn.itcast.ssm.shiro.MyFormAuthenticationFilter">
添加驗證碼:
[html] view plain copy print?
<TR>
<TD>驗證碼:</TD>
<TD><input id="randomcode" name="randomcode" size="8" /> <img
id="randomcode_img" src="${baseurl}validatecode.jsp" alt=""
width="56" height="20" align='absMiddle' /> <a
href=javascript:randomcode_refresh()>刷新</a></TD>
</TR>
修改applicationContext-shiro.xml:
用戶登陸選擇“自動登陸”本次登陸成功會向cookie寫身份信息,下次登陸從cookie中取出身份信息實現自動登陸。
向cookie記錄身份信息需要用戶身份信息對象實現序列化接口,如下:
[html] view plain copy print?
<!-- 安全管理器 -->
<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生效時間30天 -->
<property name="maxAge" value="2592000" />
</bean>
修改formAuthenticationFitler添加頁面中“記住我checkbox”的input名稱:
[html] view plain copy print?
<bean id="formAuthenticationFilter"
class="cn.itcast.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>
在login.jsp中添加“記住我”checkbox。
[html] view plain copy print?
<TR>
<TD></TD>
<TD>
<input type="checkbox" name="rememberMe" />自動登陸
</TD>
</TR>
過濾器簡稱 | 對應的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 沒有參數,表示可以匿名使用。
authc:例如/admins/user/**=authc表示需要認證(登錄)才能使用,沒有參數
roles:例子/admins/user/**=roles[admin],參數可以寫多個,多個時必須加上引號,并且參數之間用逗號分割,當有多個參數時,例如admins/user/**=roles["admin,guest"],每個參數通過才算通過,相當于hasAllRoles()方法。
perms:例子/admins/user/**=perms[user:add:*],參數可以寫多個,多個時必須加上引號,并且參數之間用逗號分割,例如/admins/user/**=perms["user:add:*,user:modify:*"],當有多個參數時必須每個參數都通過才通過,想當于isPermitedAll()方法。
rest:例子/admins/user/**=rest[user],根據請求的方法,相當于/admins/user/**=perms[user:method] ,其中method為post,get,delete等。
port:例子/admins/user/**=port[8081],當請求的url的端口不是8081是跳轉到schemal://serverName:8081?queryString,其中schmal是協議http或https等,serverName是你訪問的host,8081是url配置里port的端口,queryString
是你訪問的url里的?后面的參數。
authcBasic:例如/admins/user/**=authcBasic沒有參數表示httpBasic認證
ssl:例子/admins/user/**=ssl沒有參數,表示安全的url請求,協議為https
user:例如/admins/user/**=user沒有參數表示必須存在用戶,當登入操作時不做檢查
注:
anon,authcBasic,auchc,user是認證過濾器,
perms,roles,ssl,rest,port是授權過濾器
Jsp頁面添加:
<%@ tagliburi="http://shiro.apache.org/tags" prefix="shiro" %>
標簽名稱 | 標簽條件(均是顯示標簽內容) |
<shiro:authenticated> | 登錄之后 |
<shiro:notAuthenticated> | 不在登錄狀態時 |
<shiro:guest> | 用戶在沒有RememberMe時 |
<shiro:user> | 用戶在RememberMe時 |
<shiro:hasAnyRoles name="abc,123" > | 在有abc或者123角色時 |
<shiro:hasRole name="abc"> | 擁有角色abc |
<shiro:lacksRole name="abc"> | 沒有角色abc |
<shiro:hasPermission name="abc"> | 擁有權限資源abc |
<shiro:lacksPermission name="abc"> | 沒有abc權限資源 |
<shiro:principal> | 顯示用戶身份名稱 |
<shiro:principal property="username"/> 顯示用戶身份中的屬性值
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。