[JAVA] Spring Security Usage memo Method security

Basic and mechanical story Authentication / Authorization Story Remember-Me story CSRF story Session management story The story of the response header CORS story The story of Run-As The story of ACL Test story Talk about cooperation with MVC and Boot

Extra edition What Spring Security can and cannot do


Spring Security supports method-level access control in addition to URL-specified access control. It is recommended that method-level access control be performed at the service layer without relying solely on URL-based access control.

http://docs.spring.io/spring-security/site/docs/4.2.1.RELEASE/reference/htmlsingle/#request-matching

In practice we recommend that you use method security at your service layer, to control access to your application, and do not rely entirely on the use of security constraints defined at the web-application level. [Translation] In practice, we recommend that you use method security at the service layer to control access to your application and do not completely trust the use of security constraints defined at the web application level.

URLs change and it is difficult to take account of all the possible URLs that an application might support and how requests might be manipulated. [Translation] It is difficult to think about how to handle all URLs and requests that your application supports when the URL changes.

Security defined at the service layer is much more robust and harder to bypass, so you should always take advantage of Spring Security’s method security options. [Translation] Security defined at the service layer is more robust and difficult to bypass [^ 5]. Therefore, you should always take advantage of Spring Security's method security options.

[^ 5]: Bypassing security checks

Check before executing the method

Implementation

MyMethodSecurityService.java


package sample.spring.security.service;

import org.springframework.security.access.prepost.PreAuthorize;

public class MyMethodSecurityService {
    
    @PreAuthorize("hasAuthority('ADMIN')")
    public String execute() {
        return "Hello Method Security!!";
    }
}

MyMethodSecurityServlet.java


package sample.spring.security.servlet;

import org.springframework.security.access.AccessDeniedException;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import sample.spring.security.service.MyMethodSecurityService;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

@WebServlet("/method-security")
public class MyMethodSecurityServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        WebApplicationContext context = WebApplicationContextUtils.getRequiredWebApplicationContext(req.getServletContext());
        MyMethodSecurityService service = context.getBean(MyMethodSecurityService.class);

        PrintWriter writer = resp.getWriter();
        try {
            writer.println(service.getMessage());
        } catch (AccessDeniedException e) {
            writer.println(e.getMessage());
        } finally {
            writer.close();
        }
    }
}

namespace

applicationContext.xml


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:sec="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-3.0.xsd
         http://www.springframework.org/schema/security
         http://www.springframework.org/schema/security/spring-security.xsd">
    
    <sec:global-method-security pre-post-annotations="enabled" />

    <bean class="sample.spring.security.service.MyMethodSecurityService" />

    <sec:http>
        <sec:intercept-url pattern="/login" access="permitAll" />
        <sec:intercept-url pattern="/**" access="isAuthenticated()" />
        <sec:form-login />
        <sec:logout />
    </sec:http>

    <sec:authentication-manager>
        <sec:authentication-provider>
            <sec:user-service>
                <sec:user name="user" password="user" authorities="USER" />
                <sec:user name="admin" password="admin" authorities="ADMIN" />
            </sec:user-service>
        </sec:authentication-provider>
    </sec:authentication-manager>
</beans>

Java Configuration

MySpringSecurityConfig.java


package sample.spring.security;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import sample.spring.security.service.MyMethodSecurityService;

@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled=true)
public class MySpringSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/login").permitAll()
                .anyRequest().authenticated()
                .and()
            .formLogin();
    }
    
    @Bean
    public MyMethodSecurityService myMethodSecurityService() {
        return new MyMethodSecurityService();
    }

    @Autowired
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                .withUser("user")
                .password("user")
                .authorities("USER")
            .and()
                .withUser("admin")
                .password("admin")
                .authorities("ADMIN");
    }
}

Operation check

The top is when you log in as ʻuser, and the bottom is when you log in as ʻadmin.

spring-security.jpg

Description

applicationContext.xml


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

MySpringSecurityConfig.java


@EnableGlobalMethodSecurity(prePostEnabled=true)
public class MySpringSecurityConfig extends WebSecurityConfigurerAdapter {

--To enable method security --For namespace, add <global-method-security> tag and set pre-post-annotations =" enabled " --For Java Configuration, add @EnableGlobalMethodSecurity and set prePostEnabled = true

MyMethodSecurityService.java


    @PreAuthorize("hasAuthority('ADMIN')")
    public String getMessage() {
        return "Hello Method Security!!";
    }

--Annotate the method you want to apply security with @PreAuthorize --value specifies expression-based access control, similar to access in<intercept-url>

Put a check process after the method

Implementation

MyMethodSecurityService.java


package sample.spring.security.service;

import org.springframework.security.access.prepost.PostAuthorize;

public class MyMethodSecurityService {
    
    @PostAuthorize("returnObject == 'hoge'")
    public String getMessage(String parameter) {
        System.out.println("parameter = " + parameter);
        return parameter;
    }
}

MyMethodSecurityServlet.java


package sample.spring.security.servlet;

...

@WebServlet("/method-security")
public class MyMethodSecurityServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        WebApplicationContext context = WebApplicationContextUtils.getRequiredWebApplicationContext(req.getServletContext());
        MyMethodSecurityService service = context.getBean(MyMethodSecurityService.class);

        PrintWriter writer = resp.getWriter();
        try {
            String parameter = req.getParameter("parameter");
            writer.println(service.getMessage(parameter));
        } catch (AccessDeniedException e) {
            writer.println(e.getMessage());
        } finally {
            writer.close();
        }
    }
}

The settings are the same as [previous one](# method-security-pre-authorize).

Operation check

Access / method-security? Parameter = hoge

Server output


parameter = hoge

spring-security.jpg

Access / method-security? Parameter = fuga

Server output


parameter = fuga

spring-security.jpg

Description

MyMethodSecurityService.java


    @PostAuthorize("returnObject == 'hoge'")
    public String getMessage(String parameter) {
        System.out.println("parameter = " + parameter);
        return parameter;
    }

--If you annotate with @PostAuthorize, the check will be done after the method is executed. --Specify expression-based access control for value as well as @ PreAuthorize --You can refer to the return value of the method with the name returnObject

Refer to method arguments in an expression

Implementation

MyMethodSecurityService.java


package sample.spring.security.service;

import org.springframework.security.access.prepost.PreAuthorize;

public class MyMethodSecurityService {

    @PreAuthorize("#strValue == 'aaa' and #intValue == 1")
    public String getMessage(String strValue, int intValue) {
        return "strValue=" + strValue + ", intValue=" + intValue;
    }
}

MyMethodSecurityServlet.java


package sample.spring.security.servlet;

...

@WebServlet("/method-security")
public class MyMethodSecurityServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        WebApplicationContext context = WebApplicationContextUtils.getRequiredWebApplicationContext(req.getServletContext());
        MyMethodSecurityService service = context.getBean(MyMethodSecurityService.class);

        PrintWriter writer = resp.getWriter();
        try {
            String strValue = req.getParameter("strValue");
            int intValue = Integer.parseInt(req.getParameter("intValue"));

            writer.println(service.getMessage(strValue, intValue));
        } catch (AccessDeniedException e) {
            writer.println(e.getMessage());
        } finally {
            writer.close();
        }
    }
}

Operation check

Access / method-security? StrValue = abc & intValue = 1

spring-security.jpg

Access / method-security? StrValue = aaa & intValue = 1

spring-security.jpg

Description

MyMethodSecurityService.java


    @PreAuthorize("#strValue == 'aaa' and #intValue == 1")
    public String getMessage(String strValue, int intValue) {

--You can refer to the parameters by prefixing them with #.

How to resolve method parameter names

The parameter name of the method cannot be taken by reflection if it is Java 7 or less. On the other hand, in Java 8 or above, if the -parameters option is specified at compile time, parameter names can be taken by reflection /api/java/lang/reflect/Parameter.html).

Spring offers a variety of parameter name solutions to handle these situations.

Specify with annotation

python


package sample.spring.security.service;

import org.springframework.security.access.method.P;
import org.springframework.security.access.prepost.PreAuthorize;

public class MyMethodSecurityService {
    
    @PreAuthorize("#strValue == 'aaa' and #intValue == 1")
    public String getMessage(@P("strValue") String strValue, @P("intValue") int intValue) {
        return "strValue=" + strValue + ", intValue=" + intValue;
    }
}

An annotation called @ P is prepared. Now you can annotate the parameter and specify the parameter name with value to get the parameter name via annotation.

The implementation class is ʻAnnotationParameterNameDiscoverer`.

Get by reflection

In Java 8 and above, it can be used when compiling with the -parameters option specified at compile time.

The implementation class is StandardReflectionParameterNameDiscoverer.

Parse the class file to retrieve the parameter names

To use this function, it is necessary to generate debug information at compile time. Specifically, it is necessary to specify at least vars with the -g option.

By the way, if you are compiling with Gradle, this option is enabled by default, so you can use this function without worrying about it.

The implementation class is LocalVariableTableParameterNameDiscoverer.


The above functions are applied in the order of "annotation"-> "reflection"-> "class file analysis", and the name found first is used.

reference

Recommended Posts

Spring Security Usage memo Method security
Spring Security usage memo CSRF
Spring Security usage memo Run-As
Spring Security usage memo Remember-Me
Spring Security usage memo CORS
Spring Security usage memo test
Spring Security usage memo Authentication / authorization
Spring Security usage memo response header
Spring Security usage memo session management
Spring Security usage memo Basic / mechanism
Spring Security Usage Memo Domain Object Security (ACL)
Spring Shell usage memo
Spring boot controller method memo
Spring Security usage memo: Cooperation with Spring MVC and Boot
Spring retrospective memo
JavaParser usage memo
WatchService usage memo
PlantUML usage memo
Spring Boot + Thymeleaf BootStrap installation method memo
JUnit5 usage memo
JJUG CCC Spring 2018 memo
About Spring Security authentication
Spring boot memo writing (1)
Java learning memo (method)
[Java ~ Method ~] Study memo (5)
Spring Security causes 403 forbidden
Spring boot memo writing (2)
Call your own method with PreAuthorize in Spring Security
Spring Framework self-study memo series_1
Java Silver Study Method Memo
Create a java method [Memo] [java11]
Login function with Spring Security
[Spring Security] Spring Security on GAE (SE)
Dependency Management Plugin Usage memo
Try using Spring Boot Security
Ruby design pattern template method pattern memo
Implemented authentication function with Spring Security ②
◆ Spring Boot + gradle environment construction memo
Spring Boot + PostgreSQL error resolution method
Implemented authentication function with Spring Security ③
Implemented authentication function with Spring Security ①
Learn Spring Security authentication processing architecture
String output method memo in Ruby
Memo after the first Spring project-MVC-
A memo that touched Spring Boot
Spring thorough introduction version upgrade memo
Authentication / authorization with Spring Security & Thymeleaf
Memo after the first Spring project-Database-
[Java] Processing time measurement method memo
Thymeleaf usage notes in Spring Boot
Spring5 MVC Web application development with Visual Studio Code Spring Security usage 1/3 [Preparation]