一,权限管理解决方案
1)粗颗粒度和细颗粒度
(1)概念
对资源类型的管理称为粗颗粒度权限管理,即只控制到菜单、按钮、方法,粗粒度的例子比如:用户具有用户管理的权限,具有导出订单明细的权限。对资源实例的控制称为细颗粒度权限管理,即控制到数据级别的权限,比如:用户只允许修改本部门的员工信息,用户只允许导出自己创建的订单明细。
(2)如何实现粗颗粒度和细颗粒度权限管理
对于粗颗粒度的权限管理可以很容易做系统架构级别的功能,即系统功能操作使用统一的粗颗粒度的权限管理。对于细颗粒度的权限管理不建议做成系统架构级别的功能,因为对数据级别的控制是系统的业务需求,随着业务需求的变更业务功能变化的可能性很大,建议对数据级别的权限控制在业务层个性化开发,比如:用户只允许修改自己创建的商品信息可以在service接口添加校验实现,service接口需要传入当前操作人的标识,与商品信息创建人标识对比,不一致则不允许修改商品信息。
2)基于url拦截
基于url拦截是企业中常用的权限管理方法,实现思路是:将系统操作的每个url配置在权限表中,将权限对应到角色,将角色分配给用户,用户访问系统功能通过Filter进行过虑,过虑器获取到用户访问的url,只要访问的url是用户分配角色中的url则放行继续访问。
如下图:
3)使用权限管理框架
对于权限管理基本上每个系统都有,使用权限管理框架完成权限管理功能的开发可以节省系统开发时间,并且权限管理框架提供了完善的认证和授权功能有利于系统扩展维护,但是学习权限管理框架是需要成本的,所以选择一款简单高效的权限管理框架显得非常重要。
二,基于url拦截实现
1)这里我们创建一个用户的身份实体ActiveUser,用来存放用户的身份信息,在用户登录成功后将该身份信息存放到session当中。
//用户登陆请求
@RequestMapping("/loginsubmit")
public String loginsubmit(HttpSession session,String usercode,String password,String randomcode) throws Exception{
//校验验证码
//从session获取正确验证码
String validateCode = (String)session.getAttribute("validateCode");
if(!randomcode.equals(validateCode)){
//抛出异常:验证码错误
throw new CustomException("验证码 错误 !");
}
//用户身份认证
ActiveUser activeUser = sysService.authenticat(usercode, password);
//登录成功将用户信息记录到session
session.setAttribute("activeUser", activeUser);
//跳转到首页
return "redirect:first.action";
}
//退出请求
@RequestMapping("/logout")
public String logout(HttpSession httpSession) throws Exception{
//清空session
httpSession.invalidate();
return "redirect:first.action";
2、公开访问地址配置:
对于不需要用户认证就可以访问的地址信息进行配置,这里我们可以单独写一个配置文件进行配置,后边读取判断。
#公开访问地址
login.action=登录页面
loginsubmit.action=登录请求
3、公共访问地址配置:
对于只要用户认证通过就可以访问的地址信息进行配置,这里我们同样也是通过一个配置文件进行配置,后边通过读取判断。
#公共访问地址
first.action=首页
logout.action=退出
4、认证拦截器:
通过认证拦截器对用户身份信息进行判断。
package com.example.interceptor;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import com.example.domain.ActiveUser;
import com.example.util.ResourcesUtil;
public class LoginInterceptor implements HandlerInterceptor {
//在执行handler之前来执行的
//用于用户认证校验、用户权限校验
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
//得到请求的url
String url = request.getRequestURI();
//判断是否是公开 地址
//实际开发中需要公开 地址配置在配置文件中
//从配置中取逆名访问url
List<String> open_urls = ResourcesUtil.gekeyList("anonymousURL");
//遍历公开 地址,如果是公开 地址则放行
for(String open_url:open_urls){
if(url.indexOf(open_url)>=0){
//如果是公开 地址则放行
return true;
}
}
//判断用户身份在session中是否存在
HttpSession session = request.getSession();
ActiveUser activeUser = (ActiveUser) session.getAttribute("activeUser");
//如果用户身份在session中存在放行
if(activeUser!=null){
return true;
}
//执行到这里拦截,跳转到登陆页面,用户进行身份认证
request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request, response);
//如果返回false表示拦截不继续执行handler,如果返回true表示放行
return false;
}
//在执行handler返回modelAndView之前来执行
//如果需要向页面提供一些公用 的数据或配置一些视图信息,使用此方法实现 从modelAndView入手
@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("HandlerInterceptor1...postHandle");
}
//执行handler之后执行此方法
//作系统 统一异常处理,进行方法执行性能监控,在preHandle中设置一个时间点,在afterCompletion设置一个时间,两个时间点的差就是执行时长
//实现 系统 统一日志记录
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex)
throws Exception {
System.out.println("HandlerInterceptor1...afterCompletion");
}
}
5、授权拦截器
通过授权拦截器,判断用户是否具有访问资源的权限。
package com.example.interceptor;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import cn.itcast.ssm.po.ActiveUser;
import cn.itcast.ssm.po.SysPermission;
import cn.itcast.ssm.util.ResourcesUtil;
/**
*
* <p>Title: HandlerInterceptor1</p>
* <p>Description: 授权拦截器</p>
* <p>Company: www.itcast.com</p>
* @author 传智.燕青
* @date 2015-3-22下午4:11:44
* @version 1.0
*/
public class PermissionInterceptor implements HandlerInterceptor {
//在执行handler之前来执行的
//用于用户认证校验、用户权限校验
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
//得到请求的url
String url = request.getRequestURI();
//判断是否是公开 地址
//实际开发中需要公开 地址配置在配置文件中
//从配置中取逆名访问url
List<String> open_urls = ResourcesUtil.gekeyList("anonymousURL");
//遍历公开 地址,如果是公开 地址则放行
for(String open_url:open_urls){
if(url.indexOf(open_url)>=0){
//如果是公开 地址则放行
return true;
}
}
//从配置文件中获取公共访问地址
List<String> common_urls = ResourcesUtil.gekeyList("commonURL");
//遍历公用 地址,如果是公用 地址则放行
for(String common_url:common_urls){
if(url.indexOf(common_url)>=0){
//如果是公开 地址则放行
return true;
}
}
//获取session
HttpSession session = request.getSession();
ActiveUser activeUser = (ActiveUser) session.getAttribute("activeUser");
//从session中取权限范围的url
List<SysPermission> permissions = activeUser.getPermissions();
for(SysPermission sysPermission:permissions){
//权限的url
String permission_url = sysPermission.getUrl();
if(url.indexOf(permission_url)>=0){
//如果是权限的url 地址则放行
return true;
}
}
//执行到这里拦截,跳转到无权访问的提示页面
request.getRequestDispatcher("/WEB-INF/jsp/refuse.jsp").forward(request, response);
//如果返回false表示拦截不继续执行handler,如果返回true表示放行
return false;
}
//在执行handler返回modelAndView之前来执行
//如果需要向页面提供一些公用 的数据或配置一些视图信息,使用此方法实现 从modelAndView入手
@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("HandlerInterceptor1...postHandle");
}
//执行handler之后执行此方法
//作系统 统一异常处理,进行方法执行性能监控,在preHandle中设置一个时间点,在afterCompletion设置一个时间,两个时间点的差就是执行时长
//实现 系统 统一日志记录
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex)
throws Exception {
System.out.println("HandlerInterceptor1...afterCompletion");
}
}
6、配置拦截器
将拦截器配置起来,使其工作。
<!--拦截器 -->
<mvc:interceptors>
<mvc:interceptor>
<!-- 用户认证拦截 -->
<mvc:mapping path="/**" />
<bean class="com.example.interceptor.LoginInterceptor"></bean>
</mvc:interceptor>
<mvc:interceptor>
<!-- 授权拦截 -->
<mvc:mapping path="/**" />
<bean class="com.example..interceptor.PermissionInterceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>
总结
这里主要是通过两个拦截器实现了认证和授权,其优点是可以不必依赖于框架实现,对于拦截器我们也可以通过web提供的filter实现;缺点在于对于系统配置很多的URL,或者在系统初始化时将URL设置到数据库中,再有就是对于访问地址的变动要同时改变配置,维护相对不易。