在几乎所有的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.servlet.handler.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.
下面这段代码我不是很清楚能不能详细的说明一下,他们的作用,万分感谢!
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; Reply
1. 不明白你的意思, 本文使用的正是重写preHandle()方法. 本文中如果用户授权失败, 会提示, 然后跳转.
2. GroupMapping 本质上是一个哈希表(散列表, 键值对集合), 保存了URL(支持通配符)与组名的对应关系. 当用户访问某个URL时, Interceptor中查询该URL对应的组, 然后检查用户是否属于该组.
Reply
1. 为什么不只用preHandle,如果group为普通用户则跳转到首页,如果是管理员则跳转到后台管理页。
2. 你的properties groupMapping和PathMatcher恕小弟浅薄,可否详细说明一下groupMapping的内容是什么?
谢谢! Reply