在幾乎所有的web應用中都需要對訪問者(用戶)進行權限管理, 因為我們希望某些頁面只對特定的用戶開放, 以及某些操作只有符合身份的用戶才能進行. 這之中涉及到了身份驗證和權限管理. 只有單用戶系統和多用戶單權限系統才不需要權限管理.
在本文中, 使用了基于組的權限管理, 并在Spring框架下利用HandlerInterceptorAdapter和Hibernate進行實現.
User的結構是:
public class User {
private int id;
private String name;
private String password;
private Set<String> groups = new HashSet<String>();
}
UserGroup表:
user:int group:String
使用聯合主鍵, 在Java中沒有對應的類.
Hibernate映射文件是:
<hibernate-mapping auto-import="true" default-lazy="false">
<class name="net.ideawu.User" table="User">
<cache usage="read-write" />
<id name="id" column="id">
<generator class="native"/>
</id>
<property name="name" column="name"/>
<property name="password" column="password"/>
<set name="groups" table="UserGroup" cascade="save-update" lazy="false">
<key column="user" />
<element column="`group`" type="string" />
</set>
</class>
</hibernate-mapping>
一切的身份驗證交給一個繼承HandlerInterceptorAdapter的類來做:
import org.springframework.web.util.UrlPathHelper;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
...
public class AuthorizeInterceptor extends HandlerInterceptorAdapter {
private UrlPathHelper urlPathHelper = new UrlPathHelper();
private PathMatcher pathMatcher = new AntPathMatcher();
private Properties groupMappings;
/**
* Attach URL paths to group.
*/
public void setGroupMappings(Properties groupMappings) {
this.groupMappings = groupMappings;
}
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
String url = urlPathHelper.getLookupPathForRequest(request);
String group = lookupGroup(url); // 找出資源所需要的權限, 即組名
if(group == null){ // 所請求的資源不需要保護.
return true;
}
// 如果已經登錄, 一個User實例被保存在session中.
User loginUser = (User)request.getSession().getAttribute("loginUser");
ModelAndView mav = new ModelAndView("system/authorizeError");
if(loginUser == null){
mav.addObject("errorMsg", "你還沒有登錄!");
throw new ModelAndViewDefiningException(mav);
}else{
if(!loginUser.getGroups().contains(group)){
mav.addObject("errorMsg", "授權失敗! 你不在 <b>" + group + "</b> 組!");
throw new ModelAndViewDefiningException(mav);
}
return true;
}
}
/*
* 查看 org.springframework.web.servlet.handler.AbstractUrlHandlerMapping.lookupHandler()
* Ant模式的最長子串匹配法.
*/
private String lookupGroup(String url){
String group = groupMappings.getProperty(url);
if (group == null) {
String bestPathMatch = null;
for (Iterator it = this.groupMappings.keySet().iterator(); it.hasNext();) {
String registeredPath = (String) it.next();
if (this.pathMatcher.match(registeredPath, url) &&
(bestPathMatch == null || bestPathMatch.length() <= registeredPath.length())) {
group = this.groupMappings.getProperty(registeredPath);
bestPathMatch = registeredPath;
}
}
}
return group;
}
}
下面我們需要在Spring的應用上下文配置文件中設置:
<bean id="authorizeInterceptor" class="net.ideawu.AuthorizeInterceptor"> <property name="groupMappings"> <value> <!-- Attach URL paths to group --> /admin/*=admin </value> </property> </bean> <bean id="simpleUrlHandlerMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> <property name="interceptors"> <list> <ref bean="authorizeInterceptor" /> </list> </property> <property name="mappings"> <value> /index.do=indexController /browse.do=browseController /admin/removeArticle.do=removeArticleController </value> </property> </bean>
注意到"/admin/*=admin", 所以/admin目錄下的所有資源只有在admin組的用戶才能訪問, 這樣就不用擔心普通訪客刪除文章了. 使用這種方法, 你不需要在removeArticleController中作身份驗證和權限管理, 一切都交給AuthorizeInterceptor.