SpringMVC拦截器+Spring自定义注解实现权限验证

来源:互联网 发布:福彩3d算法 编辑:程序博客网 时间:2024/05/20 16:34

设计思路

主要针对需要登录后操作的接口进行校验。接入层在对外暴露接口后,网页、APP、第三方等等途径进行访问接口。用户请求首先会被SpringMVC拦截器拦截到,在拦截器里第一步就是需要校验用户的登录身份(由于是分布式系统这里采用的是userId+accessToken方式来校验),登录校验通过之后再进行用户权限校验,此时会自动拦截@AuthValidate注解的method(核心),如果权限校验失败则抛出权限不足异常,否则校验通过之后再执行具体接口并返回结果。

1、自定义注解

复制代码
 1 package com.mao.auth; 2 import java.lang.annotation.Documented; 3 import java.lang.annotation.ElementType; 4 import java.lang.annotation.Retention; 5 import java.lang.annotation.RetentionPolicy; 6 import java.lang.annotation.Target; 7  8 /** 9  * 10  * 项目名称:---11  * 模块名称:接入层12  * 功能描述:权限定义13  * 创建人: mao2080@sina.com14  * 创建时间:2017年5月9日 下午8:41:0515  * 修改人: mao2080@sina.com16  * 修改时间:2017年5月9日 下午8:41:0517  */18 @Target(value = ElementType.METHOD)19 @Retention(value = RetentionPolicy.RUNTIME)20 @Documented21 public @interface AuthValidate {22     23     /**24      * 25      * 描述:权限定义26      * @author mao2080@sina.com27      * @created 2017年5月8日 上午11:36:4128      * @since 29      * @return 权限代码30      */31     AuthCode value() default AuthCode.Allow;32     33 }
复制代码

2、权限枚举

复制代码
  1 package com.mao.auth;  2   3 /**  4  *   5  * 项目名称:---  6  * 模块名称:接入层  7  * 功能描述:权限类型枚举  8  * 创建人: mao2080@sina.com  9  * 创建时间:2017年5月8日 上午11:43:12 10  * 修改人: mao2080@sina.com 11  * 修改时间:2017年5月8日 上午11:43:12 12  */ 13 public enum AuthCode { 14      15     Allow("00000", "00000", "允许访问"), 16      17     /******************客户权限******************/ 18      19     AU0001("100001", "AU0001", "新增用户", "新增用户"), 20      21     AU0002("100002", "AU0002", "删除用户", "批量删除用户"); 22      23     /**权限标识 */ 24     private String authId; 25      26     /**权限编码 */ 27     private String authCode; 28      29     /**权限名称 */ 30     private String authName; 31      32     /**权限描述 */ 33     private String authDesc; 34      35     /** 36      *  37      * 描述:构建设备类型 38      * @author mao2080@sina.com 39      * @created 2017年3月22日 上午13:50:58 40      * @since  41      * @param authId 权限标识 42      * @param authCode 权限编码 43      * @param authName 权限名称 44      * @return 45      */ 46     private AuthCode(String authId, String authCode, String authName) { 47         this.authId = authId; 48         this.authCode = authCode; 49         this.authName = authName; 50     } 51      52     /** 53      *  54      * 描述:构建设备类型 55      * @author mao2080@sina.com 56      * @created 2017年3月22日 上午13:50:58 57      * @since  58      * @param authId 权限标识 59      * @param authCode 权限编码 60      * @param authName 权限名称 61      * @param authDesc 权限描述 62      * @return 63      */ 64     private AuthCode(String authId, String authCode, String authName, String authDesc) { 65         this.authId = authId; 66         this.authCode = authCode; 67         this.authName = authName; 68         this.authDesc = authDesc; 69     } 70      71     public String getAuthId() { 72         return authId; 73     } 74  75     public void setAuthId(String authId) { 76         this.authId = authId; 77     } 78  79     public String getAuthCode() { 80         return authCode; 81     } 82  83     public void setAuthCode(String authCode) { 84         this.authCode = authCode; 85     } 86  87     public String getAuthDesc() { 88         return authDesc; 89     } 90  91     public void setAuthDesc(String authDesc) { 92         this.authDesc = authDesc; 93     } 94      95     public String getAuthName() { 96         return authName; 97     } 98  99     public void setAuthName(String authName) {100         this.authName = authName;101     }102 103     @Override104     public String toString() {105         return String.format("authId:%s, authCode:%s, authName:%s, authDesc:%s", this.authId, this.authCode, this.authName, this.authDesc);106     }107 108 }
复制代码

3、Controller使用自定义注解

复制代码
 1 package com.mao.controller; 2  3 import javax.servlet.http.HttpServletRequest; 4  5 import org.apache.commons.logging.Log; 6 import org.apache.commons.logging.LogFactory; 7 import org.springframework.stereotype.Controller; 8 import org.springframework.web.bind.annotation.RequestMapping; 9 import org.springframework.web.bind.annotation.ResponseBody;10 11 import com.mao.auth.AuthCode;12 import com.mao.auth.AuthValidate;13 import com.mao.beans.ResObject;14 import com.mao.exception.BusinessException;15 16 /**17  * 18  * 项目名称:---19  * 模块名称:接入层20  * 功能描述:用户控制层21  * 创建人: mao2080@sina.com22  * 创建时间:2017年5月9日 下午8:15:5023  * 修改人: mao2080@sina.com24  * 修改时间:2017年5月9日 下午8:15:5025  */26 @Controller27 @RequestMapping("/userController")28 public class UserController {29 30     /**日志*/31     @SuppressWarnings("unused")32     private static final Log loger = LogFactory.getLog(UserController.class);33     34      /**35       * 36       * 描述:新增用户37       * @author mao2080@sina.com38       * @created 2017年5月9日 下午8:16:4139       * @since 40       * @param request41       * @return42       * @throws BusinessException43       */44     @RequestMapping("/createUser")45     @ResponseBody46     @AuthValidate(AuthCode.AU0001)47     public ResObject createUser(HttpServletRequest request) throws BusinessException{48         //业务代码49         return new ResObject();50     }51     52     /**53       * 54       * 描述:新增用户55       * @author mao2080@sina.com56       * @created 2017年5月9日 下午8:16:4157       * @since 58       * @param request59       * @return60       * @throws BusinessException61       */62     @RequestMapping("/deleteUser")63     @ResponseBody64     @AuthValidate(AuthCode.AU0002)65     public ResObject deleteUser(HttpServletRequest request) throws BusinessException{66         //业务代码67         return new ResObject();68     }69 }
复制代码

4、SpringMVC拦截器

复制代码
  1 package com.mao.interceptor;  2 import java.io.PrintWriter;  3 import java.util.ArrayList;  4 import java.util.List;  5   6 import javax.servlet.http.HttpServletRequest;  7 import javax.servlet.http.HttpServletResponse;  8   9 import org.springframework.web.method.HandlerMethod; 10 import org.springframework.web.servlet.HandlerInterceptor; 11 import org.springframework.web.servlet.ModelAndView; 12  13 import com.mao.auth.AuthCode; 14 import com.mao.auth.AuthValidate; 15 import com.mao.exception.BusinessException; 16 import com.mao.util.JsonUtil; 17  18 /** 19  *  20  * 项目名称:--- 21  * 模块名称:接入层 22  * 功能描述:用户登录拦截器(利用SpringMVC自定义拦截器实现) 23  * 创建人: mao2080@sina.com 24  * 创建时间:2017年4月25日 下午8:53:49 25  * 修改人: mao2080@sina.com 26  * 修改时间:2017年4月25日 下午8:53:49 27  */ 28 public class UserLoginInterceptor implements HandlerInterceptor { 29      30     /** 31      *  32      * 描述:构造函数 33      * @author mao2080@sina.com 34      * @created 2017年4月28日 下午5:20:34 35      * @since  36      * @param accessService 37      */ 38     public UserLoginInterceptor() { 39          40     } 41  42     /** 43      *  44      * 描述:执行方法前 45      * @author mao2080@sina.com 46      * @created 2017年4月25日 下午9:01:44 47      * @since  48      * @param request HttpServletRequest 49      * @param response HttpServletResponse 50      * @param handler handler 51      * @return 52      * @throws Exception 53      */ 54     @Override 55     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { 56         try { 57             //校验登录 58             this.userLoginValidate(request); 59             //校验权限 60             this.userAuthValidate(request, handler); 61         } catch (Exception e) { 62             e.printStackTrace(); 63             printMessage(response, e); 64             return false; 65         } 66         return true; 67     } 68      69     /** 70      *  71      * 描述:输出到前端 72      * @author mao2080@sina.com 73      * @created 2017年4月28日 上午11:00:25 74      * @since  75      * @param response 响应 76      * @param res 对象 77      * @throws Exception 78      */ 79     public static void printMessage(HttpServletResponse response, Object res) throws Exception{ 80         PrintWriter writer = null; 81         response.setCharacterEncoding("UTF-8"); 82         response.setContentType("text/html; charset=utf-8"); 83         try { 84             writer = response.getWriter(); 85             writer.print(JsonUtil.toJson(res)); 86         } catch (Exception e) { 87             e.printStackTrace(); 88         } finally { 89             if (writer != null){ 90                 writer.close(); 91             } 92         } 93     } 94      95     @Override 96     public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { 97          98     } 99 100     @Override101     public void afterCompletion(HttpServletRequest request,    HttpServletResponse response, Object handler, Exception ex)    throws Exception {102         103     }104     105     /**106      * 107      * 描述:用户登录校验108      * @author mao2080@sina.com109      * @created 2017年5月9日 下午8:27:25110      * @since 111      * @param request112      * @throws BusinessException113      */114     private void userLoginValidate(HttpServletRequest request) throws BusinessException {115         //校验代码116     }117     118     /**119      * 120      * 描述:用户权限校验121      * @author mao2080@sina.com122      * @created 2017年5月4日 下午8:34:09123      * @since 124      * @param request HttpServletRequest125      * @param handler 126      * @return127      * @throws BusinessException128      */129     private void userAuthValidate(HttpServletRequest request, Object handler) throws BusinessException {130         AuthValidate validate = ((HandlerMethod) handler).getMethodAnnotation(AuthValidate.class);131         if(validate == null){132             throw new BusinessException("未配置自定义注解");133         }134         String funcCode = validate.value().getAuthCode();135         if(funcCode.equals(AuthCode.Allow.getAuthCode())){136             return;137         }138         String authId = validate.value().getAuthId();139         List<String> auths = new ArrayList<>();//模拟从缓存或者从数据库中查询出对应用户的权限140         if(!auths.contains(authId)){141             throw new BusinessException("权限不足");142         }143     }144 145 }
复制代码

5、拦截器配置

复制代码
 1 package com.mao.interceptor; 2  3 import org.apache.commons.logging.Log; 4 import org.apache.commons.logging.LogFactory; 5 import org.springframework.context.annotation.ComponentScan; 6 import org.springframework.context.annotation.Configuration; 7 import org.springframework.web.servlet.config.annotation.EnableWebMvc; 8 import org.springframework.web.servlet.config.annotation.InterceptorRegistration; 9 import org.springframework.web.servlet.config.annotation.InterceptorRegistry;10 import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;11 12 /**13  * 14  * 项目名称:---15  * 模块名称:接入层16  * 功能描述:拦截器配置17  * 创建人: mao2080@sina.com18  * 创建时间:2017年5月9日 下午8:54:0019  * 修改人: mao2080@sina.com20  * 修改时间:2017年5月9日 下午8:54:0021  */22 @Configuration23 @ComponentScan(basePackages={"com.mao"})24 @EnableWebMvc25 public class WebConfiguration extends WebMvcConfigurerAdapter {26     27     /**日志*/28     private static final Log loger = LogFactory.getLog(WebConfiguration.class);29     30     /**31      * 32      * 描述:构造函数33      * @author mao2080@sina.com34      * @created 2017年5月3日 下午4:48:4135      * @since36      */37     public WebConfiguration() {38         loger.info("开启系统登录拦截");39     }40 41     /**42      * 43      * 描述:添加拦截器44      * @author mao2080@sina.com45      * @created 2017年4月25日 下午8:50:5446      * @since 47      * @param registry48      */49     @Override50     public void addInterceptors(InterceptorRegistry registry) {51         this.excludeUserLogin(registry.addInterceptor(new UserLoginInterceptor()));52     }53     54     /**55      * 56      * 描述:拦截请求57      * @author mao2080@sina.com58      * @created 2017年5月9日 下午8:55:2859      * @since 60      * @param registration61      */62     public void excludeUserLogin(InterceptorRegistration registration){63         registration.addPathPatterns("/userController/*");64     }65     66 }
复制代码

6、返回对象

复制代码
  1 package com.mao.beans;  2   3 import java.io.Serializable;  4   5 /**  6  *   7  * 项目名称:  8  * 模块名称:  9  * 功能描述: 10  * 创建人: mao2080@sina.com 11  * 创建时间:2017年5月3日 下午6:37:11 12  * 修改人: mao2080@sina.com 13  * 修改时间:2017年5月3日 下午6:37:11 14  */ 15 public class ResObject implements Serializable{ 16      17     /**序列号*/ 18     private static final long serialVersionUID = 589903502110209046L; 19  20     /**返回代码*/ 21     private int code = 200; 22      23     /**返回信息*/ 24     private String desc = "Success."; 25      26     /**返回数据*/ 27     private Object data; 28  29     /** 30      *  31      * 构建函数 32      * @author mao2080@sina.com 33      * @created 2017年3月24日 下午4:25:23 34      * @since 35      */ 36     public ResObject() { 37          38     } 39      40     /** 41      *  42      * 描述:构造函数 43      * @author mao2080@sina.com 44      * @created 2017年4月18日 下午3:32:26 45      * @since  46      * @param data 数据 47      */ 48     public ResObject(Object data) { 49         super(); 50         this.data = data; 51     } 52      53     /** 54      *  55      * 构建函数 56      * @author mao2080@sina.com 57      * @created 2017年3月24日 下午4:25:35 58      * @since  59      * @param code 返回代码 60      * @param desc 返回信息 61      */ 62     public ResObject(int code, String desc) { 63         super(); 64         this.code = code; 65         this.desc = desc; 66     } 67  68     /** 69      *  70      * 构建函数 71      * @author mao2080@sina.com 72      * @created 2017年3月24日 下午4:25:39 73      * @since  74      * @param code 返回代码 75      * @param desc 返回信息 76      * @param data 返回数据 77      */ 78     public ResObject(int code, String desc, Object data) { 79         super(); 80         this.code = code; 81         this.desc = desc; 82         this.data = data; 83     } 84  85     public Object getData() { 86         return data; 87     } 88  89     public void setData(Object data) { 90         this.data = data; 91     } 92  93     public int getCode() { 94         return code; 95     } 96  97     public void setCode(int code) { 98         this.code = code; 99     }100 101     public String getDesc() {102         return desc;103     }104 105     public void setDesc(String desc) {106         this.desc = desc;107     }108 109 }
复制代码

7、异常类

复制代码
 1 package com.mao.exception; 2  3 /** 4  *  5  * 项目名称:--- 6  * 模块名称:接入层 7  * 功能描述:异常类 8  * 创建人: mao2080@sina.com 9  * 创建时间:2017年5月9日 下午8:22:2110  * 修改人: mao2080@sina.com11  * 修改时间:2017年5月9日 下午8:22:2112  */13 public class BusinessException extends Exception{14 15     public BusinessException() {16         17     }18 19     public BusinessException(String message) {20          super(message);21     }22     23 }
复制代码

8、json工具类

复制代码
  1 package com.mao.util;  2   3 import com.alibaba.dubbo.common.utils.StringUtils;  4 import com.alibaba.fastjson.JSON;  5 import com.alibaba.fastjson.TypeReference;  6 import com.alibaba.fastjson.parser.Feature;  7 import com.alibaba.fastjson.serializer.SerializerFeature;  8 import com.mao.exception.BusinessException;  9  10 /** 11  *  12  * 项目名称:--- 13  * 模块名称:常用工具类 14  * 功能描述:json工具类 15  * 创建人: mao2080@sina.com 16  * 创建时间:2017年3月28日 上午11:56:15 17  * 修改人: mao2080@sina.com 18  * 修改时间:2017年3月28日 上午11:56:15 19  */ 20 public class JsonUtil { 21  22     /** 23      *  24      * 描述:将对象格式化成json字符串 25      * @author mao2080@sina.com 26      * @created 2017年4月1日 下午4:38:18 27      * @since  28      * @param object 对象 29      * @return json字符串 30      * @throws BusinessException  31      */ 32     public static String toJson(Object object) throws BusinessException { 33         try { 34             return JSON.toJSONString(object, new SerializerFeature[] { 35                 SerializerFeature.WriteMapNullValue,  36                 SerializerFeature.DisableCircularReferenceDetect,  37                 SerializerFeature.WriteNonStringKeyAsString }); 38         } catch (Exception e) { 39             throw new BusinessException(); 40         } 41     } 42  43     /** 44      *  45      * 描述:将对象格式化成json字符串(PrettyFormat格式) 46      * @author mao2080@sina.com 47      * @created 2017年4月1日 下午4:38:18 48      * @since  49      * @param object 对象 50      * @return json字符串 51      * @throws BusinessException  52      */ 53     public static String toJsonFormat(Object object) throws BusinessException { 54         try { 55             return JSON.toJSONString(object, new SerializerFeature[] { 56                 SerializerFeature.WriteMapNullValue,  57                 SerializerFeature.PrettyFormat,  58                 SerializerFeature.DisableCircularReferenceDetect,  59                 SerializerFeature.WriteNonStringKeyAsString }); 60         } catch (Exception e) { 61             throw new BusinessException(); 62         } 63     } 64  65     /** 66      *  67      * 描述:转Map 68      * @author mao2080@sina.com 69      * @created 2017年4月1日 下午5:00:20 70      * @since  71      * @param obj 对象 72      * @return object 73      * @throws BusinessException  74      */ 75     public static Object toJsonObject(Object obj) throws BusinessException { 76         try { 77             return JSON.toJSON(obj); 78         } catch (Exception e) { 79             throw new BusinessException(); 80         } 81     } 82  83     /** 84      *  85      * 描述:将json串转为对象 86      * @author mao2080@sina.com 87      * @created 2017年4月1日 下午5:01:23 88      * @since  89      * @param jsonString json串 90      * @param clazz 对象 91      * @return 92      * @throws BusinessException  93      */ 94     public static <T> T fromJson(String jsonString, Class<T> clazz) throws BusinessException { 95         try { 96             if (StringUtils.isBlank(jsonString)) { 97                 return null; 98             } 99             return (T) JSON.parseObject(jsonString, clazz);100         } catch (Exception e) {101             throw new BusinessException();102         }103     }104 105     /**106      * 107      * 描述:暂时不开通108      * @author mao2080@sina.com109      * @created 2017年4月1日 下午5:08:12110      * @since 111      * @param jsonString112      * @return113      * @throws Exception114      */115     @SuppressWarnings("unused")116     private static <T> T fromJson(String jsonString) throws Exception {117         return JSON.parseObject(jsonString, new TypeReference<T>() {118         }, new Feature[0]);119     }120     121 }
复制代码