1. 程式人生 > 實用技巧 >API安全(九)-授權

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、在使用者實體中新增許可權欄位

/**
 */
@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);
    }

}