特別提示:本人博客部分有參考網絡其他博客,但均是本人親手編寫過並驗證通過。如發現博客有錯誤,請及時提出以免誤導其他人,謝謝!歡迎轉載,但記得標明文章出處:
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 }