Apache Shiro入門總結
一:數據庫表結構
users:用戶表,包含用戶名、密碼、角色ID。
roles:角色表,包含角色名。
permission:權限表,包含權限字符串(user:add),權限描述。
role_permission:中間表,包含角色和權限的id。
二:SecurityManager和Subject
SecurityManager是shiro的核心,它是一個facade,所有的交互最終都通過SecurityManager執行。Subject代表當前應用的參與者,可以是一個用戶,也可以是第三方服務等其他抽象參與者。一般的shiro操作都是操作Subject就行。下面是一些常用方法。
Subject currentUser = SecurityUtils.getSubject();//獲取當前用戶
currentUser.getSession(); //獲取會話
currentUser.checkPermission("printer:print");//檢查是否有對應權限
currentUser.checkRole("管理員");//檢查是否有對應角色
currentUser.logout(); //執行登出操作,移除對應的session以及權限數據。
三:Wildcard Permissions
shiro默認使用一種類似"printer:print:lp7200"的語法來標識權限。這三部分分別為域、動作、實例。一般使用域+動作就可以了,如刪除用戶,可以用"user:delete"。
通配符"*"和","可以用在任何一個部分。如"printer:*:lp7200,jet40",表示可以在打印機lp7200、jet40上執行所有操作。
四:自定義Realm
Realm是一個用于訪問諸如用戶、角色、權限這類安全數據的組件。實際應用中這些數據一般保存在數據庫里,因此需要自定義realm實現安全數據的訪問。雖然shiro自身提供了JdbcRealm,但是這需要數據庫表設計和shiro默認的一致,開發者可根據實際情況選擇。
package com.my.shiro.dao;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.shiro.authc.AccountException;
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.AuthorizationException;
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 com.my.shiro.model.Permission;
import com.my.shiro.model.User;
public class MyJdbcRealm extends AuthorizingRealm {
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
if (principals == null) {
throw new AuthorizationException("PrincipalCollection method argument cannot be null.");
}
String username = (String) getAvailablePrincipal(principals);
User user = User.dao.getByUserName(username);
int roleid = user.getInt("role_id");
List<Permission> perms = Permission.dao.getPermissions(roleid);
Set<String> roleSet = new HashSet<String>();
roleSet.add(String.valueOf(roleid));
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(roleSet);
info.setStringPermissions(getPermissionSet(perms));
return info;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(
AuthenticationToken token) throws AuthenticationException {
UsernamePasswordToken upToken = (UsernamePasswordToken) token;
String username = upToken.getUsername();
if (username == null) {
throw new AccountException("Null usernames are not allowed by this realm.");
}
String password = User.dao.getPwdByUserName(username);
if(password==null)
throw new AccountException("account error...");
AuthenticationInfo info = new SimpleAuthenticationInfo(username, password, getName());
return info;
}
private Set<String> getPermissionSet(List<Permission> perms){
Set<String> set = new HashSet<String>();
for(Permission p:perms){
set.add(p.getStr("pkey"));
}
return set;
}
} doGetAuthenticationInfo(AuthenticationToken token)是根據用戶名從數據庫獲取用戶名、密碼組成一個AuthenticationInfo,此時還未進行驗證,該對象用于之后和用戶提交的密碼(未加密、加密)進行匹配。
doGetAuthorizationInfo(PrincipalCollection principals)此時已經驗證成功,此方法根據用戶名獲取對應的角色和權限。
五:web.xml配置
<filter>
<filter-name>ShiroFilter</filter-name>
<filter-class>org.apache.shiro.web.servlet.ShiroFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>ShiroFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping> 這個是shiro1.2后的filter配置。此filter建議放在其他filter之前,使shiro能正常工作。
六:shiro.ini配置
包含main,users,roles,urls四個section。當使用數據庫存儲時,users,roles在這里用不到。main用于配置SecurityManager及其依賴,如Realm。urls是web應用特有的,用于根據url設置訪問權限。
[main] myRealm=com.my.shiro.dao.MyJdbcRealm securityManager.realms=$myRealm user.loginUrl=/ perms.unauthorizedUrl=/common/unauthor.jsp [urls] /common/**=anon /security/**=user /admin/**=user,perms["admin:*"]user.loginUrl定義未驗證時的跳轉路徑,perms.unauthorizedUrl定義無權限時的跳轉路徑。
urls區域中,左邊為訪問的url路徑,右邊為過濾器,可以有多個形成過濾器鏈。shiro提供了一些默認的過濾器。
七:登錄
public void doLogin(){
String username = getPara("username");
String password = getPara("password");
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
Subject user = SecurityUtils.getSubject();
try{
user.login(token);
}catch (AuthenticationException e) {
redirect(getRequest().getContextPath()+"/");
return;
}
//登錄成功
redirect(getRequest().getContextPath()+"/page/index");
}八:方法級的權限控制 使用上面的url配置可以基于路徑進行粗顆粒度的權限控制,shiro提供了一些注釋,配合其他AOP框架,可以實現方法級的權限控制。如:
@RequiresAuthentication
@RequiresGuest
@RequiresPermissions("account:create")
@RequiresUser 九:JSP Taglib 權限控制
<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
<shiro:authenticated>
<a href="updateAccount.jsp">Update your contact information</a>.
</shiro:authenticated>