首先看一下Shiro中的web filter过滤器:
默认采用的认证过滤器filter是表单过滤器,默认登录的url是/login(只要没有认证的都会跳转到/login路径下),辅助登录成功url是/first。
默认登录url跳转到的页面是login.jsp如下:
[html] view plain copy print ? <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <%@ page contentType="text/html; charset=UTF-8"%> <%@ include file="/WEB-INF/jsp/tag.jsp"%> <html> <head> <TITLE>药品采购平台</TITLE> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> <LINK rel="stylesheet" type="text/css" href="${baseurl}styles/style.css"> <LINK rel="stylesheet" type="text/css" href="${baseurl}styles/login.css"> <LINK rel="stylesheet" type="text/css" href="${baseurl}js/easyui/themes/default/easyui.css"> <LINK rel="stylesheet" type="text/css" href="${baseurl}js/easyui/themes/icon.css"> <STYLE type="text/css"> .btnalink { cursor: hand; display: block; width: 80px; height: 29px; float: left; margin: 12px 28px 12px auto; line-height: 30px; background: url('${baseurl}images/login/btnbg.jpg') no-repeat; font-size: 14px; color: #fff; font-weight: bold; text-decoration: none; } </STYLE> <%@ include file="/WEB-INF/jsp/common_js.jsp"%> <script type="text/javascript"> //登录提示方法 function loginsubmit() { $("#loginform").submit(); } </SCRIPT> </HEAD> <BODY style="background: #f6fdff url(${baseurl}images/login/bg1.jpg) repeat-x;"> <FORM id="loginform" name="loginform" action="" method="post"> <DIV class="logincon"> <DIV class="title"> <IMG alt="" src="${baseurl}images/login/logo.png"> </DIV> <DIV class="cen_con"> <IMG alt="" src="${baseurl}images/login/bg2.png"> </DIV> <DIV class="tab_con"> <input type="password" style="display:none;" /> <TABLE class="tab" border="0" cellSpacing="6" cellPadding="8"> <TBODY> <TR> <TD>用户名:</TD> <TD colSpan="2"><input type="text" id="usercode" name="username" style="WIDTH: 130px" /></TD> </TR> <TR> <TD>密 码:</TD> <TD><input type="password" id="pwd" name="password" style="WIDTH: 130px" /> </TD> </TR> <%-- <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> --%> <TR> <TD colSpan="2" align="center"><input type="button" class="btnalink" onclick="loginsubmit()" value="登 录" /> <input type="reset" class="btnalink" value="重 置" /></TD> </TR> </TBODY> </TABLE> </DIV> </DIV> </FORM> </BODY> </HTML> form过滤器有个特点就是,只要是表单提交(条件:1.post 2.action路径为"")就相当于: Subject currentUser = SecurityUtils.getSubject(); currentUser.login(token);他会自动到Real中的方法进行身份认证:
[html] view plain copy print ? /** * 身份认证 */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { String userName = (String) token.getPrincipal(); User user = userService.findByUsername(userName); if(user == null) { //抛出用户不存在异常 throw new UnknownAccountException();//没找到帐号 } if(user.getLocked()) { //抛出用户被锁定异常 throw new LockedAccountException(); //帐号锁定 } // 如果查询到返回认证信息AuthenticationInfo SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(userName, user.getPassword(),ByteSource.Util.bytes(user.getCredentialsSalt()), this.getName()); return simpleAuthenticationInfo; } 值得注意的是SimpleAuthenticationInfo这个方法的构造函数,因为它决定了凭证认证的方式:1.
[html] view plain copy print ? public SimpleAuthenticationInfo(Object principal, Object credentials, String realmName) { this.principals = new SimplePrincipalCollection(principal, realmName); this.credentials = credentials; } 该构造器对应的默认任凭类,什么都不需要输入,没有加密算法,没有迭代次数,直接通过用户名和密码进行进行验证就可以。 [html] view plain copy print ? <bean id="userRealm" class="com.lgy.web.shiro.UserRealm"> <!-- 设置认证凭证器 --> <!--<property name="credentialsMatcher" ref="credentialsMatcher" /> --> </bean>2.
[html] view plain copy print ? public SimpleAuthenticationInfo(Object principal, Object hashedCredentials, ByteSource credentialsSalt, String realmName) { this.principals = new SimplePrincipalCollection(principal, realmName); this.credentials = hashedCredentials; this.credentialsSalt = credentialsSalt; } 这个和你加密的密码salt有关: [html] view plain copy print ? package com.lgy.service; import org.apache.shiro.crypto.RandomNumberGenerator; import org.apache.shiro.crypto.SecureRandomNumberGenerator; import org.apache.shiro.crypto.hash.SimpleHash; import org.apache.shiro.util.ByteSource; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import com.lgy.model.User; @Service public class PasswordHelper { private RandomNumberGenerator randomNumberGenerator = new SecureRandomNumberGenerator(); @Value("${password.algorithmName}") private String algorithmName; @Value("${password.hashIterations}") private int hashIterations; public void encryptPassword(User user) { user.setSalt(randomNumberGenerator.nextBytes().toHex()); String newPassword = new SimpleHash( algorithmName, //加密算法 user.getPassword(), //密码 ByteSource.Util.bytes(user.getCredentialsSalt()), //salt盐 username + salt hashIterations //迭代次数 ).toHex(); user.setPassword(newPassword); } } 所以需要设置凭证信息: [html] view plain copy print ? <!-- Realm实现 --> <bean id="userRealm" class="com.lgy.web.shiro.UserRealm"> <!-- 设置认证凭证器 --> <property name="credentialsMatcher" ref="credentialsMatcher" /> </bean> <!-- 认证凭证器 --> <bean id="credentialsMatcher" class="org.apache.shiro.authc.credential.HashedCredentialsMatcher"> <!-- 算法名称 --> <property name="hashAlgorithmName" value="${password.algorithmName}" /> <!-- 迭代次数 --> <property name="hashIterations" value="${password.hashIterations}" /> </bean> 若认证通过后,它会跳转到设置的辅助登录成功url是/first。身份认证就到这里结束!
授权过程如下:
shiro授权有三种方式
Shiro 支持三种方式的授权:
1 编程式:通过写if/else 授权代码块完成:
Subject subject =SecurityUtils.getSubject();
if(subject.hasRole(“admin”)) {
//有权限
} else {
//无权限
}
2 注解式:通过在执行的Java方法上放置相应的注解完成:
@RequiresRoles("admin")
public void hello() {
//有权限
}
3.JSP/GSP 标签:在JSP/GSP 页面通过相应的标签完成:
<shiro:hasRolename="admin">
<!— 有权限—>
</shiro:hasRole>
编程试的不用说了,重点说说注解方式和jsp标签方式:
若使用SpringMVC注解试,需要在SpringMVC的配置文件中配置注解启动:
[html] view plain copy print ? <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:util="http://www.springframework.org/schema/util" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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 http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <aop:config proxy-target-class="true"></aop:config> <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor"> <property name="securityManager" ref="securityManager"/> </bean> </beans> 在控制器中: [html] view plain copy print ? @RequiresPermissions("user:create") @RequestMapping(value = "/create", method = RequestMethod.GET) public String showCreateForm(Model model) { //... return "user/edit"; } 当进入到这个Controller中的时候,会先进入realm中的: [html] view plain copy print ? /** * 授权认证 */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { User user = (User) principals.getPrimaryPrincipal(); SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo(); authorizationInfo.setRoles(userService.findRoles(user.getUsername())); authorizationInfo.setStringPermissions(userService.findPermissions(user.getUsername())); return authorizationInfo; } 权限比较可能有如下2个:@RequiresPermissions("user:create") @RequiresRoles("admin")
1.基于角色的认证
2.基于权限码的认证
若使用jsp标签进行认证:
条件:需要导入<%@taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
页面中
<shiro:hasPermission name="user:update">
...... </shiro:hasPermission> <shiro:hasRole name="">
...... </shiro:hasRole>
同上进入该页面中时候,若出现这样的标签,每出现一个都会调用realm中的:
[html] view plain copy print ? /** * 授权认证 */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { User user = (User) principals.getPrimaryPrincipal(); SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo(); authorizationInfo.setRoles(userService.findRoles(user.getUsername())); authorizationInfo.setStringPermissions(userService.findPermissions(user.getUsername())); return authorizationInfo; }相当于他们调用了shiro中的:
Subject subject = SecurityUtils.getSubject(); subject.checkRole(""); subject.checkPermission("");
*
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:principalproperty="username"/> 显示用户身份中的属性值
当然每次这么做可能浪费的性能很不好,需要配置缓存。