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
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();
}
}
}
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");
}
}
The top is when you log in as ʻuser, and the bottom is when you log in as ʻadmin
.
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>
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).
Access / method-security? Parameter = hoge
Server output
parameter = hoge
Access / method-security? Parameter = fuga
Server output
parameter = fuga
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
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();
}
}
}
Access / method-security? StrValue = abc & intValue = 1
Access / method-security? StrValue = aaa & intValue = 1
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 #
.
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.
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`.
In Java 8 and above, it can be used when compiling with the -parameters
option specified at compile time.
The implementation class is StandardReflectionParameterNameDiscoverer
.
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.
Recommended Posts