security for Spring Boot - roles configuration on Postgresql

Để cung cấp service , chúng ta cần 1 giải pháp để giải quết vấn đề bảo mật truy cập đến các api, java annotation chính là giải pháp cho bài toán này.

Chúng ta cùng tìm hiểu chi tiết của giải pháp này.

Hệ thống được base với spring boot. (đây là 1 framework tuyệt vời), sử dụng với postgres schema.

Với spring boot bạn có thể thao tác dễ dàng cấu hình connect databse với file application.property.

spring.jpa.database=POSTGRESQL
spring.datasource.platform=postgres
spring.jpa.show-sql=true
spring.database.driverClassName=org.postgresql.Driver
spring.datasource.url=jdbc:postgresql://localhost:5432/mydatabase
spring.datasource.testWhileIdle = true
spring.password=123456

Để cấu hình service của bạn connect database, chỉ cần tạo duy nhất 1 file application.property trong folder resource của project. Tất cả việc còn lại sẽ do spring-boot take care. Thay vì việc tạo ra nhiều file cấu hình trong project , tất cả cấu hình sẽ được thiết lập tập trung trong file này.

Quay lại với chủ đề của chúng ta, làm sao để mỗi khi có 1 request call đến ứng dụng , chúng ta có thể block các request không mong muốn. Chẳng hạn bạn chỉ mong muốn cho phép người đã login vào hệ thống, với những role là Admin được phép tạo dữ liệu (create data).

Để giải quyết vấn đề này, chúng ta cần tạo ra 1 lớp để filter toàn bộ các request gọi đến api. Đối với các project j2ee thông thường , bạn có thể sử dụng class filter để xử lý sercurity inter ứng dụng của mình. Nhưng đối với phát triển service, chúng ta cần 1 cách khác để giải quyết vấn đề này.

Annotation: Tôi đã tạo ra 2 annotation @Access@Role

@Pass : cho phép mọi request đến các api

@Role: chỉ cho phép các user có role được call đến service

Example:

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Pass
{
}
}
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Allow
{
    Role[] roles() default {};
}

Bạn chỉ cần đặt @pass phía trên các api or class cho phép access mà không cần sercurity. Và tương tự đặt @Role với các api or class cần hạn chế truy cập.

Example:

@Controller
public class EmployeeController
{

    @Autowired
    private EmloyeeService emloyeeService;

    @PASS
    @RequestMapping(value = "/getAllEmployee",RequestMethod.GET)
    @ResponseBody
    public Account getALLEmployee(HttpSession session) throws ServiceException
    {
        return emloyeeService.getAllEmployeer();
    }

    @Role(Role.Admin)
    @RequestMapping(value = "/createEmployee",RequestMethod.POST)
    @ResponseBody
    public Account createEmployee(@RequestBody EmployeeDTO accountDTO, HttpSession session) throws InvocationTargetException, IllegalAccessException, ValidateException, IOException, ServiceException
    {
        return emloyeeService.createEmployee(EmployeeDTO accountDTO);
    }

Với thiết lập trên, tất cả các request có role là Admin sẽ được gọi đến service tạo Employee.

Và class quan trọng nhất chính là SercurityInterceptor, class được extend từ HandlerInterceptorAdapter , có rất nhiều resource được public trên mạng về các giải pháp áp dụng, chúng ta chỉ cần quan tầm đến 3 method chính:

PreHandle,PostHandle , AfterCompletion : tương ứng với các thao tác sử lý authen trước, sau, và khi kết thúc intercepted method. Đối với SecurityInterceptor tôi đã override Prehandle method để kiểm tra role hợp lệ:

@Component
public class AuthenticationInterceptor extends HandlerInterceptorAdapter
{
// ------------------------------ FIELDS ------------------------------

// --------------------- Interface HandlerInterceptor ---------------------

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception
    {

        if (AppConfig.isDebug()) return true;

        HandlerMethod handlerMethod = (HandlerMethod) handler;

        // If @Pass, then just allow it
        if (handlerMethod.getMethodAnnotation(ByPass.class) != null|| handlerMethod.getBeanType().getAnnotation(ByPass.class) != null)
        {
            return true;
        }

        if (request.getParameterMap().get(TOKEN) != null)
        {
            String token = request.getParameterMap().get(TOKEN)[0];
            List<Role> allowedRoleList = getAllowedRoles(handlerMethod);
            return checkToken(token, request, response, allowedRoleList);
        }
        else
        {
            throw new ResponseException(i18nMessage.NOT_LOGIN);
        }
    }

    private List<Role> getAllowedRoles(HandlerMethod handlerMethod)
    {
        something else ...
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception
    {
        super.postHandle(request, response, handler, modelAndView);
    }
 @Transactional
    private boolean checkToken(String token, HttpServletRequest request, HttpServletResponse response, List<Role> allowedRoleList) throws IOException, ResponseException, ModelAndViewDefiningException
    {
        somthing else .....
    }
}

Với class AuthenticationInterceptor, chúng ta đã kết thúc việc xây dựng class sercurity interceptor cho service của mình . Thanks you