網頁

2018/3/15

Spring MVC Controller搭配Spring Security @PreAuthorize

Spring MVC Controller的方法搭配Spring Security的@PreAuthorize控管存取權限設定如下。


首先在Spring的設定檔applicationContext.xml加入<global-method-security>

<security:global-method-security pre-post-annotations="enabled"/>

要注意的是,上面的設定必須與下面Spring MVC用來掃描@Controller的標籤放在同個context,也就是同一個配置檔中@PreAuthorize才會發生作用,例如下面統一放在Spring的配置檔applicationContext.xml

如果分別放在不同的context,例如<global-method-security>放在ApplicationContext(例如applicationContext.xml),而<mvc:annotation-driven /><context:component-scan />放在DispatcherServletWebApplicationContext(例如dispatcher-servlet.xml)則@PreAuthorize無法作用。

applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans
    xmlns:beans="http://www.springframework.org/schema/beans"
  xmlns:mvc="http://www.springframework.org/schema/mvc" 
  xmlns:context="http://www.springframework.org/schema/context"
  xmlns:security="http://www.springframework.org/schema/security"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="
        http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/mvc 
        http://www.springframework.org/schema/mvc/spring-mvc.xsd
        http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/security 
        http://www.springframework.org/schema/security/spring-security.xsd">

  <security:global-method-security pre-post-annotations="enabled"/>
  <mvc:annotation-driven />
  <context:component-scan base-package="idv.matt.controller, idv.matt.controller.api"/>
  
  <!-- Spring Security REST-->
  <security:http entry-point-ref="restAuthenticationEntryPoint">
    <security:intercept-url pattern="/api/**" access="hasRole('ROLE_ADMIN')"/>
    <security:form-login/>
    <security:logout/>
  </security:http>
  
  <security:authentication-manager alias="authenticationManager">
    <security:authentication-provider>
      <security:user-service>
        <security:user name="matt" password="matt" authorities="ROLE_ADMIN, ROLE_USER"/>
        <security:user name="john" password="john" authorities="ROLE_USER"/>
      </security:user-service>
    </security:authentication-provider>
  </security:authentication-manager>
  
  <beans:bean id="restAuthenticationEntryPoint" class="idv.matt.security.RestAuthenticationEntryPoint"/>
  
</beans:beans>

當然除上之外Spring Security的其他設定包括要驗證的url pattern等記得要設定好。

下面是要被存取的MyAPIController.hello(),在方法前加上@PreAuthorize("hasRole('ROLE_ADMIN')"),則此方法只有具有'ROLE_ADMIN'身分的使用者可以存取。

MyAPIController.java

package idv.matt.controller.api;

import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping(value="/api")
public class MyAPIController {
  
  @GetMapping(value="/hello")
  @PreAuthorize("hasRole('ROLE_ADMIN')")
  public String hello() {
    System.out.println("MyAPIController.hello()");
    
    Object principal = SecurityContextHolder.getContext()
                 .getAuthentication()
                 .getPrincipal();

    System.out.println(principal.toString());
    
    return "hello";
  }
  
}

RestAuthenticationEntryPoint.java

package idv.matt.security;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;

public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint{

  @Override
  public void commence(HttpServletRequest request, 
                      HttpServletResponse response,
                      AuthenticationException arg2) throws IOException, ServletException {

    response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
  
  }

}

根據以上設定,目前有兩個使用者matt和john。

matt因為有'ROLE_ADMIN'權限,所以登入後可以通過權限驗證存取該方法,反之john只有'ROLE_USER'權限所以存取時會回傳HTTP Status 403 - Access is denied


設定好後在瀏覽器網址列輸入http://localhost:8080/<APP_NAME>/login進入登入畫面,輸入帳號密碼登入後在瀏覽器網址列輸入http://localhost:8080/<APP_NAME>/api/hello來存取MyAPIController.hello()。登入者若是matt可以正常存取,若是john就會返回HTTP Status 403 - Access is denied


除了在xml配置檔的設定方是外,也可在Java程式中以@EnableGlobalMethodSecurity的方式設定。


參考:

沒有留言:

張貼留言