特别提示:本人博客部分有参考网络其他博客,但均是本人亲手编写过并验证通过。如发现博客有错误,请及时提出以免误导其他人,谢谢!欢迎转载,但记得标明文章出处:http://www.cnblogs.com/mao2080/
设计思路
主要针对需要登录后操作的接口进行校验。接入层在对外暴露接口后,网页、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.com 14 * 创建时间:2017年5月9日 下午8:41:05 15 * 修改人: mao2080@sina.com 16 * 修改时间:2017年5月9日 下午8:41:05 17 */ 18 @Target(value = ElementType.METHOD) 19 @Retention(value = RetentionPolicy.RUNTIME) 20 @Documented 21 public @interface AuthValidate { 22 23 /** 24 * 25 * 描述:权限定义 26 * @author mao2080@sina.com 27 * @created 2017年5月8日 上午11:36:41 28 * @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 @Override 104 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.com 22 * 创建时间:2017年5月9日 下午8:15:50 23 * 修改人: mao2080@sina.com 24 * 修改时间:2017年5月9日 下午8:15:50 25 */ 26 @Controller 27 @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.com 38 * @created 2017年5月9日 下午8:16:41 39 * @since 40 * @param request 41 * @return 42 * @throws BusinessException 43 */ 44 @RequestMapping("/createUser") 45 @ResponseBody 46 @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.com 56 * @created 2017年5月9日 下午8:16:41 57 * @since 58 * @param request 59 * @return 60 * @throws BusinessException 61 */ 62 @RequestMapping("/deleteUser") 63 @ResponseBody 64 @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 @Override 101 public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { 102 103 } 104 105 /** 106 * 107 * 描述:用户登录校验 108 * @author mao2080@sina.com 109 * @created 2017年5月9日 下午8:27:25 110 * @since 111 * @param request 112 * @throws BusinessException 113 */ 114 private void userLoginValidate(HttpServletRequest request) throws BusinessException { 115 //校验代码 116 } 117 118 /** 119 * 120 * 描述:用户权限校验 121 * @author mao2080@sina.com 122 * @created 2017年5月4日 下午8:34:09 123 * @since 124 * @param request HttpServletRequest 125 * @param handler 126 * @return 127 * @throws BusinessException 128 */ 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.com 18 * 创建时间:2017年5月9日 下午8:54:00 19 * 修改人: mao2080@sina.com 20 * 修改时间:2017年5月9日 下午8:54:00 21 */ 22 @Configuration 23 @ComponentScan(basePackages={"com.mao"}) 24 @EnableWebMvc 25 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.com 34 * @created 2017年5月3日 下午4:48:41 35 * @since 36 */ 37 public WebConfiguration() { 38 loger.info("开启系统登录拦截"); 39 } 40 41 /** 42 * 43 * 描述:添加拦截器 44 * @author mao2080@sina.com 45 * @created 2017年4月25日 下午8:50:54 46 * @since 47 * @param registry 48 */ 49 @Override 50 public void addInterceptors(InterceptorRegistry registry) { 51 this.excludeUserLogin(registry.addInterceptor(new UserLoginInterceptor())); 52 } 53 54 /** 55 * 56 * 描述:拦截请求 57 * @author mao2080@sina.com 58 * @created 2017年5月9日 下午8:55:28 59 * @since 60 * @param registration 61 */ 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:21 10 * 修改人: mao2080@sina.com 11 * 修改时间:2017年5月9日 下午8:22:21 12 */ 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.com 109 * @created 2017年4月1日 下午5:08:12 110 * @since 111 * @param jsonString 112 * @return 113 * @throws Exception 114 */ 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 }