搭建一个web系统,最先考虑的可能就是一个登录系统。登录认证授权这是一个涉及很多方面考虑的理论,关于这个问题也有很多成熟的解决方案,比如spring-security、apache shiro等。但是,在这个框架泛滥的年代,使用这些比较繁琐的框架会带来很多学习成本。由此可见登录授权问题往往就成了新手的拦路虎,或者直接没注意造成系统的安全隐患。
登录系统按照spring-security的划分,可以分为认证和授权。认证说的简单点就是把用户提交的用户名密码校验,然后把合法的用户放到session中。授权那就是区分不同的用户和角色,从而控制对特定资源的保护。
这篇文章简单说一下在springMVC环境下的一个授权实现(不是认证吆),下一篇会说一下struts2的简单授权实现。这里也给大家分享我自己录制的一个spring-security的视频教程,能力有限教程只教大家基本理论和如何看文档。
视频下载地址:http://pan.baidu.com/s/1qWGwUOO
要限制用户对某些资源的访问,最直接的肯定就是在controller中某个方法里直接判断当前用户的用户名和角色等信息。这样简单清晰实用,简直就像一个土钢炮。但是整个项目都这样写会显得特别臃肿,不够优雅。
为了打到简单易用,我们可以使用自定义Annotation,只想在一个方法上添加一个Annotation把要限制的用户名角色等信息作为参数传给Annotation,然后配合springMVC的interceptor来完成一个简单好用的授权系统。我这里仅演示一个限制角色的简单授权。其他的可以举一反三。
首先定义一个Annotation
/**
* 权限声明接口,通过声明可以访问的角色,来控制安全(多个角色是or的关系)
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Auth {
String[] roles() default {};
}
*这个Annotation可以被声明到一个controller或者是controller的某个方法上,只接受一个数组类型的参数roles,表示允许访问的角色。
仅仅一个Annotation是没有作用的,要写一个支持Annotation的拦截器(关于springMVC的拦截器大家可以找些文章看看)。
/**
* Auth Annotation支撑拦截器
*/
public class AuthSupportInterceptor implements HandlerInterceptor{
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
System.out.println("Authentication Annotation Support Handle preHandle--------------------");
//得到当前用户
HttpSession session=request.getSession();
UserInfo user=(UserInfo)session.getAttribute("user");
//当前用户角色编码
List<String> urs=new ArrayList<String>();
for(Role r:user.getRoles()){
urs.add(r.getRoleCode());
}
/*
* Auth声明支持
*/
Class c=handler.getClass();
//controller声明的处理
Auth ac=(Auth) c.getAnnotation(Auth.class);
if(ac!=null){
String[] roles=ac.roles();
for(String r:roles){
if(urs.contains(r)){
return true;
}
}
//转发到消息提示页面
request.setAttribute("message", "您没有执行该操作的权限!");
request.getRequestDispatcher("/jsp/message.jsp").forward(request, response);
return false;
}
//方法声明处理
//比对uri,找到处理的方法
String uri=request.getRequestURI();
Method[] ms=c.getMethods();
Method dealMethod=null;
for(Method m:ms){
RequestMapping rm=m.getAnnotation(RequestMapping.class);
if(rm!=null){
String[] vs = rm.value();
for(String v:vs){
if(uri.endsWith(v)){
dealMethod=m;
break;
}
}
if(dealMethod!=null){
break;
}
}
}
//判断方法有没有Auth声明
if(dealMethod!=null){
Auth au=dealMethod.getAnnotation(Auth.class);
if(au!=null){
String[] roles=au.roles();
for(String r:roles){
if(urs.contains(r)){
return true;
}
}
//转发到消息提示页面
request.setAttribute("message", "您没有执行该操作的权限!");
request.getRequestDispatcher("/jsp/message.jsp").forward(request, response);
return false;
}
}
return true;
}
public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
}
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex)
throws Exception {
}
}
*核心是这样,拦截器的preHandle会把要处理请求的handler(就是controller对象)带入,这样我们就能通过反射得到controller的Annotation来完成类上注解的支持。但是,我们并不知道如何得到要处理这个请求的具体方法。所以下面就通过URI匹配找到要处理的方法(这里比较简陋,没有完全去实现spring所支持的所有合法URL类型)。然后,再通过反射得到method上面的Annotation,然后就可以完成方法上注解的支持了!