場景java
如今有個系統,不少接口只須要登陸就能夠訪問,可是有些接口須要授予並驗證權限。若是用註解controller的方式控制接口的權限呢?數據庫
一、註解聲明代碼json
這個註解是要裝飾在controller接口上的。cookie
按照通常權限的設計,有用戶(user)-角色(role)-權限(permission)三種實體,他們之間都是多對多關係。app
註解聲明的時候,能夠配置要驗證的角色(role)或權限(menu)。因此我這裏有兩個變量。ide
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * @Author: ivan * @Description: * @Date: Created in 19:58 18/5/28 * @Modified By: */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface Authentication { long[] role() default {}; long[] menu() default {}; }
二、Authentication權限驗證Adviceui
咱們以驗證角色爲例。spa
第一步,先從controller參數的request cookie裏拿到用戶登陸信息,獲取userId,我這裏是card。設計
第二步,查詢用戶角色數據庫,獲取該user擁有哪些權限。code
第三部,跟@Authentication裏配置的權限進行比較,校驗成功返回數據,校驗失敗返回錯誤碼。
使用localthread記錄了權限驗證處理時間,用來進行監控。
/** * @Author: ivan * @Description: * @Date: Created in 20:00 18/5/28 * @Modified By: */ @Aspect @Component @Order(-10) public class AuthenticationAspect { private static Logger logger = LoggerFactory.getLogger(AuthenticationAspect.class); @Autowired private AuthDao authDao; ThreadLocal<Long> beginTime = new ThreadLocal<Long>(); @Pointcut("@annotation(authentication)") public void AuthenticationService(Authentication authentication) { } @Around("AuthenticationService(authentication)") public Object doAround(ProceedingJoinPoint joinPoint, Authentication authentication) throws Throwable{ beginTime.set(System.currentTimeMillis()); String card = null; List<Long> roleList = new ArrayList<>(); for (Object arg : joinPoint.getArgs()) { if (arg != null && arg.getClass() == RequestFacade.class) { RequestFacade request = (RequestFacade) arg; card = CookieUtils.getCardFromCookie(request); if (StringUtils.isEmpty(card)) { return JsonResult.buildFailResult(-1, 1000, "權限驗證未經過", null); } List<Role> roles = authDao.getRolesByCard(card); for (Role role : roles) { roleList.add(role.getId()); } break; } } logger.info("[authentication] user={}, roles={}", card, roleList); long[] aims = authentication.role(); boolean isPass = false; for (long aim : aims) { if (roleList.contains(aim)) { isPass = true; } } if (isPass) { logger.info("[authentication] authentication pass, cost time: {}", System.currentTimeMillis() - beginTime.get()); return joinPoint.proceed(); } else { beginTime.set(System.currentTimeMillis()); logger.info("[authentication] authentication reject, cost time: {}", System.currentTimeMillis() - beginTime.get()); return JsonResult.buildFailResult(-1, 1000, "權限驗證未經過", null); } } }
三、使用方法
一、Authentication直接裝飾在controller接口上,參數是role={2},即用戶擁有2這個角色的時候擁有訪問這個接口的權限。
二、controller第一個參數要是HttpServletRequest request,否則上面從request裏面拿用戶信息會失敗。(這個地方確實不方便)
/** * 審覈接口 * * @author ivan * @date 2018/08/02 */ @Controller @RequestMapping("/audit") public class AuditController { private static final Logger LOGGER = LoggerFactory.getLogger(AuditController.class); @Resource private AuditService auditService;
@ResponseBody @PostMapping(value = "/review") @Authentication(role = {2}) public JsonResult review(HttpServletRequest request, @RequestBody AduitDTO aduitDTO) { LOGGER.info("[aduitDTO] video service aduitDTO={}" + aduitDTO); UserInfo userInfo = CookieUtils.getLoginInfoFromCookie(request); aduitDTO.setCard(userInfo.getCard()); int status = 0; aduitDTO.setAuditor(userInfo.getCard()); JsonResult jsonResult = null; if (ListEnum.ZERO.toString().equals(aduitDTO.getType())) { if (null != aduitDTO.getStatus() && ListEnum.ONE.toString().equals(aduitDTO.getStatus())) { status = auditService.review(aduitDTO); jsonResult = JsonResult.buildSuccessResult("審覈經過"); } if (null != aduitDTO.getStatus() && ListEnum.TWO.toString().equals(aduitDTO.getStatus())) { if(StringUtils.isEmpty(aduitDTO.getReason())){ return JsonResult.buildFailResult(1, 102, "請添加不經過緣由!", null); } aduitDTO.setAuditTime(DateUtils.parseDateToStr(new Date() ,"yyyy-MM-dd HH:mm:ss")); status = auditService.updateAduit(aduitDTO); jsonResult = JsonResult.buildSuccessResult("審覈不經過"); } } return jsonResult; } }