Basic / mechanism story Authentication / Authorization Story Remember-Me story CSRF story Session management story The story of the response header Method security 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
@ tomoyukilabs's CORS Summary --Qiita was a learning experience.
Roughly summarized
--Browsers cannot access another origin from resources loaded from one origin --It seems to be the same origin policy --By setting something like "Allow requests from this origin even if they are from different origins" on the server side, the client will be able to send requests beyond the origin. --That is CORS (Cross-Origin Resource Sharing)
It feels like.
What is "origin"?Origin definition|Same origin policy-Web security| MDNSee.
Hello World
Place the following html on GitHub so that it can be opened from a browser as a static page.
python
<!doctype html>
<html>
<head>
<meta charset="UTF-8" />
<title>cors test</title>
</head>
<body>
<div>
<select id="contextPath">
<option value="namespace" selected>namespace</option>
<option value="java-config">java-config</option>
</select>
</div>
<div>
<input id="headerName" placeholder="Header-Name" />
<input id="headerValue" placeholder="Header-Value" />
</div>
<div>
<input id="responseHeaderName" placeholder="responseHeaderName" />
</div>
<button type="button" id="sendButton">Send Request</button>
<script src="https://code.jquery.com/jquery-3.2.1.min.js"
integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4="
crossorigin="anonymous"></script>
<script>
$(function() {
$('#sendButton').on('click', function() {
var headers = {};
var headerName = $('#headerName').val();
var headerValue = $('#headerValue').val();
if (headerName) {
headers[headerName] = headerValue;
}
var contextPath = $('#contextPath').val();
$.ajax({
url: 'http://localhost:8080/' + contextPath + '/cors',
type: 'GET',
dataType: 'text',
headers: headers
})
.done(function(text, status, jqXhr) {
console.log("text = " + text);
var responseHeaderName = $('#responseHeaderName').val();
if (responseHeaderName) {
console.log("[ResponseHeader] " + responseHeaderName + " : " + jqXhr.getResponseHeader(responseHeaderName));
}
})
.fail(function() {
console.error(arguments);
});
});
});
</script>
</body>
</html>
The one I actually uploaded on GitHub
I am trying to access localhost
with Ajax.
CorsServlet.java
package sample.spring.security.servlet;
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("/cors")
public class CorsServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setHeader("Original-Header", "CORS!!");
try (PrintWriter writer = resp.getWriter()) {
writer.println("Hello CORS!!");
}
}
}
An implementation that just returns the appropriate header and body of text.
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: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="hoge" password="hoge" authorities="" />
</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.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
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;
@EnableWebSecurity
public class MySpringSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/login").permitAll()
.anyRequest().authenticated()
.and()
.formLogin();
}
@Autowired
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.eraseCredentials(false)
.inMemoryAuthentication()
.withUser("hoge")
.password("hoge")
.roles();
}
}
No settings for CORS have been made.
When I click the Send Request
button on the Verification page, I get the following error: ..
I was blocked by Spring Security's authentication check and redirected to the login page.
It also says that it cannot be accessed because the ʻAccess-Control-Allow-Origin` header defined in the CORS specification is missing from the response.
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">
<bean id="corsConfiguration" class="org.springframework.web.cors.CorsConfiguration">
<property name="allowedOrigins">
<list>
<value>http://opengl-8080.github.io</value>
</list>
</property>
<property name="allowedMethods">
<list>
<value>GET</value>
</list>
</property>
</bean>
<bean id="corsSource" class="org.springframework.web.cors.UrlBasedCorsConfigurationSource">
<property name="corsConfigurations">
<map>
<entry key="/cors" value-ref="corsConfiguration" />
</map>
</property>
</bean>
<sec:http>
<sec:intercept-url pattern="/login" access="permitAll" />
<sec:intercept-url pattern="/cors" access="permitAll" />
<sec:intercept-url pattern="/**" access="isAuthenticated()" />
<sec:form-login />
<sec:logout />
<sec:cors configuration-source-ref="corsSource" />
</sec:http>
...
</beans>
Java Configuration
MySpringSecurityConfig.java
package sample.spring.security;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
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 org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
@EnableWebSecurity
public class MySpringSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/login").permitAll()
.antMatchers("/cors").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.and()
.cors()
.configurationSource(this.corsConfigurationSource());
}
private CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.addAllowedMethod("GET");
corsConfiguration.addAllowedOrigin("http://opengl-8080.github.io");
UrlBasedCorsConfigurationSource corsSource = new UrlBasedCorsConfigurationSource();
corsSource.registerCorsConfiguration("/cors", corsConfiguration);
return corsSource;
}
...
}
Run again.
This time the access is successful and the text returned by the server can be output.
applicationContext.xml
<bean id="corsSource" class="org.springframework.web.cors.UrlBasedCorsConfigurationSource">
...
</bean>
<sec:http>
...
<sec:cors configuration-source-ref="corsSource" />
</sec:http>
--CORS control is enabled by adding <cors>
under the <http>
tag.
--At this time, specify the bean of CorsConfigurationSource
that defines the detailed CORS settings in the configuration-source-ref
attribute.
MySpringSecurityConfig.java
@Override
public void configure(HttpSecurity http) throws Exception {
http...
.and()
.cors()
.configurationSource(this.corsConfigurationSource());
}
private CorsConfigurationSource corsConfigurationSource() {
...
}
--For Java Configuration, specify with .cors (). ConfigurationSource (CorsConfigurationSource)
applicationContext.xml
<bean id="corsConfiguration" class="org.springframework.web.cors.CorsConfiguration">
<property name="allowedOrigins">
<list>
<value>http://opengl-8080.github.io</value>
</list>
</property>
<property name="allowedMethods">
<list>
<value>GET</value>
</list>
</property>
</bean>
<bean id="corsSource" class="org.springframework.web.cors.UrlBasedCorsConfigurationSource">
<property name="corsConfigurations">
<map>
<entry key="/cors" value-ref="corsConfiguration" />
</map>
</property>
</bean>
--Use two classes to configure CORS
--CorsConfigurationSource
and CorsConfiguration
--Set detailed rules such as which HTTP method is allowed in CorsConfiguration
and which origin is allowed to access.
--In the case of the above settings, the HTTP method that allows the list of origins allowed by the ʻallowedOrigins property is specified by ʻallowedMethods
--CorsConfiguration
is set to ** not allow ** all requests by default, so you need to set some permission.
--CorsConfigurationSource
keeps multiple CorsConfiguration
s and determines which CorsConfiguration
should be applied to the current request
--In the above settings, ʻUrlBasedCorsConfigurationSource is used as the implementation class. --ʻUrlBasedCorsConfigurationSource
manages which CorsConfiguration
should be applied by associating it with the path pattern.
--In the case of the above settings, the rules set in the corsConfiguration
bean are applied when the / cors
is accessed.
--Path pattern can be specified in Ant format
MySpringSecurityConfig.java
private CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.addAllowedMethod("GET");
corsConfiguration.addAllowedOrigin("http://opengl-8080.github.io");
UrlBasedCorsConfigurationSource corsSource = new UrlBasedCorsConfigurationSource();
corsSource.registerCorsConfiguration("/cors", corsConfiguration);
return corsSource;
}
--For Java Configuration, you can use methods that can be set one by one, such as ʻaddAllowedMethod (String) and
registerCorsConfiguration (String, CorsConfiguration) `.
-(Although it can be used in XML, I don't think it's usually used because it only complicates the description.)
The CORS specification limits the HTTP methods and configurable headers that are allowed by default.
If you want to send a header that isn't allowed by default, you're supposed to send a prefilight request, a prefilight request to "check if it's allowed".
The client sends the production request only if the server returns "OK" to this request.
Try submitting a request with an unauthorized header called Hoge
.
I was very angry.
It says it couldn't be accessed because there was a header that wasn't allowed in the preflight request.
Check the request and response headers.
The request header says ʻAccess-Control-Request-Headers: hoge, and I'm checking if the header
hogeis allowed. However, the response does not have a header stating that
hoge` is allowed.
For this reason, the browser determines that hoge
is an unauthorized header and suspends processing.
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"
...>
<bean id="corsConfiguration" class="org.springframework.web.cors.CorsConfiguration">
<property name="allowedOrigins">
<list>
<value>http://opengl-8080.github.io</value>
</list>
</property>
<property name="allowedMethods">
<list>
<value>GET</value>
</list>
</property>
<property name="allowedHeaders">★ Add
<list>
<value>Hoge</value>
</list>
</property>
</bean>
...
</beans>
Java Configuration
MySpringSecurityConfig.java
package sample.spring.security;
...
@EnableWebSecurity
public class MySpringSecurityConfig extends WebSecurityConfigurerAdapter {
...
private CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration corsConfiguration = new CorsConfiguration();
...
corsConfiguration.addAllowedHeader("Hoge");★ Add
...
}
...
}
Hoge
is added to ʻallowedHeader of
CorsConfiguration`.
Verify the operation again.
This time the request passed properly.
Check the state of the HTTP request.
The preflight request by the ʻOPTIONSmethod is executed first, and then the request by the production
GET` is sent.
Take a look at the header towards ʻOPTIONS`.
ʻAccess-Control-Allow-Headers: hogehas been added to the response headers to indicate that the
hoge` header is allowed.
Spring Security automatically responds to preflight requests according to the permission rules set in CorsConfiguration
.
There are restrictions by default when clients access arbitrary response headers, but [exposedHeaders](http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/cors/ It can be allowed by setting CorsConfiguration.html # addExposedHeader-java.lang.String-).
Recommended Posts