API安全(九)-授权
1、授权
授权在整个安全机制中,是比较重要的一环,一般要考虑两个事情,一个是访问的请求需不需要身份认证,如果不需要直接放过,如果需要,但是没有认证,应该返回401,需要用户进行认证。另一个就是,认证了,看有没有该资源的访问权限,如果有,放行;如果没有返回403,无权限。
2、常见的访问控制
2.1、ACL :Access Control Lists,简单易用,容易实现。常见读写等少量权限控制。
2.2、RBAC:Role Based Access Control。引入角色,方便管理权限。开发交ACL复杂。
3、使用ACL实现授权控制
这里我们实现一个简单的ACL权限控制,要求,所有的请求都必须经过认证,可以为用户授予读写权限,读权限可以访问GET请求,其他请求需要有写权限。
3.1、在用户实体中添加权限字段
/** * @author caofanqi * @date 2020/1/20 13:08 */ @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过滤器 * * @author caofanqi * @date 2020/1/29 15:04 */ @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拦截器 * * @author caofanqi * @date 2020/1/29 15:31 */ @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配置类 * * @author caofanqi * @date 2020/1/28 22:32 */ @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); } }
项目源码:https://github.com/caofanqi/study-security/tree/dev-authorization