API安全(九)-授權
阿新 • • 發佈:2020-07-14
1、授權
授權在整個安全機制中,是比較重要的一環,一般要考慮兩個事情,一個是訪問的請求需不需要身份認證,如果不需要直接放過,如果需要,但是沒有認證,應該返回401,需要使用者進行認證。另一個就是,認證了,看有沒有該資源的訪問許可權,如果有,放行;如果沒有返回403,無許可權。
2、常見的訪問控制
2.1、ACL :Access Control Lists,簡單易用,容易實現。常見讀寫等少量許可權控制。
2.2、RBAC:Role Based Access Control。引入角色,方便管理許可權。開發交ACL複雜。
3、使用ACL實現授權控制
這裡我們實現一個簡單的ACL許可權控制,要求,所有的請求都必須經過認證,可以為使用者授予讀寫許可權,讀許可權可以訪問GET請求,其他請求需要有寫許可權。
3.1、在使用者實體中新增許可權欄位
/** */ @Data @Entity @Table(name = "user") public class UserDO { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(nullable = false) private String name; @Column(nullable = false,unique = true) private String username; @Column(nullable = false) private String password; /** * 使用者具有的許可權資訊,多個用逗號隔開;read讀許可權,write寫許可權 */ private String permissions; public UserDTO buildUserDTO(){ UserDTO userDTO = new UserDTO(); BeanUtils.copyProperties(this,userDTO); return userDTO; } }
3.2、使用Filter進行授權控制(這要求審計也是基於Filter實現的,保證各安全模組的執行順序)
/** * ACL過濾器 */ @Slf4j @Order(4) @Component public class AclFilter extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { log.info("++++++4、授權++++++"); /* * 要求請求都必須經過認證才能訪問 */ UserDO user = (UserDO) request.getAttribute("user"); if (user == null) { //說明沒有進行認證,返回401和WWW-Authenticate,讓瀏覽器彈出輸入框 response.setStatus(HttpStatus.UNAUTHORIZED.value()); response.setHeader("WWW-Authenticate", "Basic realm=<authentication required>"); return; } /* * 要求有對應的許可權才可以進行訪問 */ if (!hasPermission(user.getPermissions(), request.getMethod())) { response.setStatus(HttpStatus.FORBIDDEN.value()); response.getWriter().write("Forbidden"); response.getWriter().flush(); return; } filterChain.doFilter(request, response); } private boolean hasPermission(String permissions, String method) { if (StringUtils.equalsIgnoreCase(method, HttpMethod.GET.name())) { //要有讀許可權 return StringUtils.containsIgnoreCase(permissions, "read"); } else { //要有寫許可權 return StringUtils.containsIgnoreCase(permissions, "write"); } } }
3.3、啟動專案,在資料庫中為使用者賦予相應許可權如下
3.4、使用建立使用者進行測試http://127.0.0.1:9090/users
3.4.1、使用未認證的進行訪問,彈出認證框
控制檯過濾器執行順序如下
資料庫日誌如下
3.4.2、使用帶有讀許可權的tom進行認證,建立使用者失敗,返回403無許可權
3.4.3、使用帶有讀寫許可權的jack進行認證,建立使用者成功
3.3、如果審計模組使用的是攔截器實現,授權也要使用攔截器,效果一樣
/** * ACL攔截器 * */ @Slf4j //@Component public class AclInterceptor extends HandlerInterceptorAdapter { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { log.info("++++++4、授權++++++"); UserDO user = (UserDO) request.getAttribute("user"); if (user == null) { response.setStatus(HttpStatus.UNAUTHORIZED.value()); response.setHeader("WWW-Authenticate", "Basic realm=<authentication required>"); return false; } if (!hasPermission(user.getPermissions(), request.getMethod())) { response.setStatus(HttpStatus.FORBIDDEN.value()); response.getWriter().write("Forbidden"); response.getWriter().flush(); return false; } return true; } private boolean hasPermission(String permissions, String method) { if (StringUtils.equalsIgnoreCase(method, HttpMethod.GET.name())) { return StringUtils.containsIgnoreCase(permissions, "read"); } else { return StringUtils.containsIgnoreCase(permissions, "write"); } } }
/** * web配置類 * */ @Configuration public class WebConfig implements WebMvcConfigurer { @Resource private AuditLogInterceptor auditLogInterceptor; @Resource private AclInterceptor aclInterceptor; /** * 註冊攔截器,攔截器的執行順序取決於add順序 */ @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(auditLogInterceptor); registry.addInterceptor(aclInterceptor); } }