Authentication / Authorization Story Remember-Me story CSRF story Session management story The story of the response header Method security story 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
Java 1.8.0_xxx
Tomcat 8.0.35
Gradle 3.2.1
OS Windows 10
One of the projects of Spring. A framework that primarily adds security features to web applications.
It provides protection against various well-known attacks such as CSRF and session fixation attacks.
Hello World Here is written in a little more order for presentation. It may be easier to understand if you read it together with the slides (I think that it was written only on the slides).
Folder structure
|-build.gradle
`-src/main/webapp/
|-index.jsp
`-WEB-INF/
|-applicationContext.xml
`-web.xml
build.gradle
apply plugin: 'war'
sourceCompatibility = '1.8'
targetCompatibility = '1.8'
compileJava.options.encoding = 'UTF-8'
repositories {
mavenCentral()
}
dependencies {
providedCompile 'javax.servlet:javax.servlet-api:3.1.0'
compile 'javax.servlet:jstl:1.2'
compile 'org.springframework.security:spring-security-web:4.2.1.RELEASE'
compile 'org.springframework.security:spring-security-config:4.2.1.RELEASE'
}
war.baseName = 'spring-security-sample'
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>
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="ROLE_USER" />
</sec:user-service>
</sec:authentication-provider>
</sec:authentication-manager>
</beans>
index.jsp
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<title>Hello Spring Security!!</title>
</head>
<body>
<h1>Hello Spring Security!!</h1>
<c:url var="logoutUrl" value="/logout" />
<form action="${logoutUrl}" method="post">
<input type="submit" value="logout" />
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />
</form>
</body>
</html>
Generate a war file (spring-security-sample.war
) with gradle war
and deploy it to Tomcat.
After the deployment is complete, open http: // localhost: 8080 / spring-security-sample
in your browser.
A mysterious login screen is displayed.
Enter hoge
and HOGE
in User and Password, respectively, and click the Login
button.
ʻThe contents of index.jsp` are displayed.
Finally, click the logout
button.
Logout is complete and you are returned to the login screen.
build.gradle
compile 'org.springframework.security:spring-security-web:4.2.1.RELEASE'
compile 'org.springframework.security:spring-security-config:4.2.1.RELEASE'
In the minimum configuration, you can add spring-security-web
and spring-security-config
as dependencies it seems good .RELEASE / reference / htmlsingle / #gradle).
spring-security-web
It contains code related to Filter
and web apps. If you need web authentication or URL-based access control, you need this module.
The main package is ʻorg.springframework.security.web`
spring-security-config
It contains code to parse the namespace used when writing definitions in xml and code for Java config. This module is required if you want to use XML-based settings or Java configs. The main package is ʻorg.springframework.security.config` The class included here is not directly used by the application.
web.xml
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
By registering ContextLoaderListener
as a listener, Spring container (ʻApplicationContext`) is initialized.
ʻIf you do not specify what to use for the ApplicationContext class,
XmlWebApplicationContext is used by default. It is defined in ʻorg / springframework / web / context / ContextLoader.properties
of spring-web-x.x.x.RELEASE.jar
as follows, and ContextLoaderListener
is loaded when the Servlet container is initialized.
ContextLoader.properties
# Default WebApplicationContext implementation class for ContextLoader.
# Used as fallback when no explicit context implementation has been specified as context-param.
# Not meant to be customized by application developers.
org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext
By default, XmlWebApplicationContext
reads WEB-INF / applicationContext.xml
as a configuration file.
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"
...>
<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="ROLE_USER" />
</sec:user-service>
</sec:authentication-provider>
</sec:authentication-manager>
</beans>
ʻApplicationContext.xml` itself is a standard configuration file for Spring bean definition, not a special configuration file for Spring Security.
By loading http://www.springframework.org/schema/security
with xmlns
, you can use tags for Spring Security (in the reference, use this tag dedicated to Spring Security * We call it * namespace **).
You can use this tag to configure Spring Security settings on this file.
The above settings roughly define the following:
--Anyone can access / login
(permitAll
)
--Access to all other paths (/ **
) must be authenticated (ʻisAuthenticated ()) --Login method is Form login --Allow logout --Define a user with the role
ROLE_USER with name =
hoge, password =
HOGE` as user information
The individual tags will be described in a little more detail below.
<http>
When you define the <http>
tag, some beans are automatically registered in the container.
Among them, the following two classes are important beans.
FilterChainProxy
SecurityFilterChain
FilterChainProxy
is registered in the container with the bean name "springSecurityFilterChain"
. </ span>
This class is the gateway to Spring Security processing.
SecurityFilterChain
is just an interface in itself, and DefaultSecurityFilterChain
is registered in the container as an implementation class.
As the name suggests, SecurityFilterChain
is a chain of javax.servlet.Filter
that has a security function, and holds multiple Filter
s inside.
Spring Security realizes the security function by using Filter
.
As you can see from the name FilterChainProxy
, the class itself does nothing.
Specific processing is delegated to the Filter
s of the SecurityFilterChain
.
<intercept-url>
Conditions (access control) required for access are defined for each URL pattern.
The pattern
attribute can be described in ant path format.
In the ʻaccess attribute, specify the conditions required to access the URL specified in the
patternattribute.
permitAll means to allow all access (no authentication required). And ʻisAuthenticated ()
means to allow access if authenticated (logged in).
The ʻaccess` attribute is described using Spring's own expression language called Spring Expression Language (SpEL).
<form-login>
Defines that Form authentication is required.
In case of authentication error, you will be redirected to / login
by default.
By default, accessing / login
will take you to a simple login page provided by Spring Security.
This default login page is generated by DefaultLoginPageGeneratingFilter
in spring-security-web-x.x.x.RELEASE.jar
.
<logout>
By adding this tag, you will be able to log out.
By default, a POST request to / logout
will log you out.
<authentication-manager>
ʻDefine AuthenticationManager as a bean. ʻAuthenticationManager
is a class that executes authentication processing and must be defined.
ʻAuthenticationManager itself is an interface, and the implementation class uses
ProviderManager.
ProviderManager itself does not perform specific authentication processing, but delegates the authentication processing to ʻAuthenticationProvider
, which will be described later.
<authentication-provider>
ʻDefine AuthenticationProvider` as a bean. This interface provides a specific authentication process according to the type of authentication.
For example, the class that provides LDAP authentication processing is like LdapAuthenticationProvider
.
If this tag is declared, DaoAuthenticationProvider
will be registered in the container.
This class gets user information from ʻUserDetailsService` and performs authentication processing.
<user-service>
ʻRegister UserDetailsService as a bean. This interface provides the ability to retrieve detailed user information (ʻUserDetails
).
Declaring this tag will register ʻInMemoryUserDetailsManager` in the container. As the name implies, this class holds user information in memory. So to speak, it is a temporary implementation for checking the operation.
<user>
ʻDefine an instance of UserDetails`. This interface defines Getter methods and other methods for accessing detailed user information.
Declaring this tag creates an instance of the class ʻUser`.
The name
, password
, ʻauthorities` attributes allow you to specify names, passwords, and permissions to identify users.
web.xml
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Define DelegatingFilterProxy
as a Servlet filter in web.xml
.
At this time, <filter-name>
is defined with the name " springSecurityFilterChain "
.
Now Spring Security will be applied when the URL defined by <filter-mapping>
is accessed.
DelegatingFilterProxy
gets a bean that implements javax.servlet.Filter
from the Spring container using the name set in its own <filter-name>
.
And it is a servlet filter that just delegates processing to that bean.
Here, " springSecurityFilterChain "
is specified for <filter-name>
.
This name is the same as the FilterChainProxy
bean name that is automatically registered in the container when you use the<http>
tag in ʻapplicationContext.xml`. I am doing it.
In other words, DelegatingFilterProxy
is responsible for bridging the Servlet filter and Spring Security (FilterChainProxy
).
index.jsp
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<title>Hello Spring Security!!</title>
</head>
<body>
<h1>Hello Spring Security!!</h1>
<c:url var="logoutUrl" value="/logout" />
<form action="${logoutUrl}" method="post">
<input type="submit" value="logout" />
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />
</form>
</body>
</html>
By default, CSRF measures are enabled. Therefore, when sending a request, it is necessary to pass a token for CSRF countermeasures together.
Token values and parameter names are stored in the request scope as _csrf
.
A more detailed explanation of CSRF measures will be provided at a later date. .. ..
Summarize how the process is flowing behind the scenes between starting the server and logging in.
I'm using a sequence diagram, but it's not bad because it doesn't follow the strict UML notation for clarity.
Although it is not strict in part, I think that the initialization → authentication process is performed in the following atmosphere.
ContextLoaderListener
registered as Listener
in web.xml
is executed.XmlWebApplicationContext
is created and /WEB-INF/applicationContext.xml
is loaded.<http>
tag registers an instance of FilterChainProxy
with the Spring container with the name "" springSecurityFilterChain "
.DelegateFilterProxy
registered as Filter
in web.xml
is generated by the Servlet container.DelegateFilterProxy
is called to get the bean from the Spring container with its own name (" springSecurityFilterChain "
) (FilterChainProxy
is obtained).FilterChainProxy
.It's also not strict, but I think the flow when redirecting to the login screen when accessing without logging in is as follows.
FilterSecurityInterceptor
calls the authentication process of ʻAuthenticationManager`. performs the authentication process, but throws ʻAuthenticationException
because it is not authenticated.LoginUrlAuthenticationEntryPoint
redirects you to the login screen.The flow from immediately after being redirected to / login
by LoginUrlAuthenticationEntryPoint
to logging in by entering the user name and password.
DefaultLoginPageGeneratingFilter
will generate the default login screen./ login
, ʻUsernamePasswordAuthenticationFilter delegates the authentication process to ʻAuthenticationManager
.ProviderManager
, which is an implementation class of ʻAuthenticationManager, delegates the authentication process to ʻAuthenticationProvider
.DaoAuthenticationProvider
, which is an implementation class of ʻAuthenticationProvider, acquires user information (ʻUserDetails
) from ʻUserDetailsService` and executes the authentication process. delegates the processing at the time of successful authentication to ʻAuthenticationSuccessFilter
. implementation class
SavedRequestAwareAuthenticationSuccessHandlerrestores and redirects the URL you were trying to access before you were taken to the login screen. --The URL you were accessing before logging in is stored in
HttpSession`.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"
...>
<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="ROLE_USER" />
</sec:user-service>
</sec:authentication-provider>
</sec:authentication-manager>
</beans>
The namespace (dedicated tag) automatically registers the various beans needed to run Spring Security in the container. Thanks to this, Spring Security settings are very simple to write.
However, when it becomes necessary to extend the authentication function, it becomes important to understand what kind of beans are registered behind the scenes and what kind of relationship they have.
To understand that, let's rewrite the configuration file created by Hello World without using namespace.
However, I will not do my best to completely match the setting in namespace, but to reproduce the series of login processes seen in Hello World. Therefore, some bean definitions are omitted.
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:util="http://www.springframework.org/schema/util"
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/util
http://www.springframework.org/schema/util/spring-util-4.3.xsd">
<bean id="springSecurityFilterChain"
class="org.springframework.security.web.FilterChainProxy">
<constructor-arg ref="securityFilterChain" />
</bean>
<bean id="securityFilterChain"
class="org.springframework.security.web.DefaultSecurityFilterChain">
<constructor-arg index="0">
<bean class="org.springframework.security.web.util.matcher.AnyRequestMatcher" />
</constructor-arg>
<constructor-arg index="1">
<list>
<ref bean="securityContextPersistenceFilter" />
<ref bean="csrfFilter" />
<ref bean="logoutFilter" />
<ref bean="usernamePasswordAuthenticationFilter" />
<ref bean="defaultLoginPageGeneratingFilter" />
<ref bean="exceptionTranslationFilter" />
<ref bean="filterSecurityInterceptor" />
</list>
</constructor-arg>
</bean>
<!--Initialize / clear SecurityContextHolder-->
<bean id="securityContextPersistenceFilter"
class="org.springframework.security.web.context.SecurityContextPersistenceFilter" />
<!-- CSRF -->
<bean id="csrfFilter"
class="org.springframework.security.web.csrf.CsrfFilter">
<constructor-arg ref="csrfTokenRepository" />
</bean>
<bean id="csrfTokenRepository"
class="org.springframework.security.web.csrf.LazyCsrfTokenRepository">
<constructor-arg>
<bean class="org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository" />
</constructor-arg>
</bean>
<!--Log out-->
<bean id="logoutFilter"
class="org.springframework.security.web.authentication.logout.LogoutFilter">
<constructor-arg index="0" value="/login?logout" />
<constructor-arg index="1">
<list>
<bean class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler" />
<bean class="org.springframework.security.web.csrf.CsrfLogoutHandler">
<constructor-arg ref="csrfTokenRepository" />
</bean>
</list>
</constructor-arg>
</bean>
<!--Login authentication-->
<bean id="usernamePasswordAuthenticationFilter"
class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
<property name="authenticationManager" ref="authenticationManager" />
<property name="authenticationFailureHandler">
<bean class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
<property name="defaultFailureUrl" value="/login?error" />
</bean>
</property>
</bean>
<!--Default login page generation-->
<bean id="defaultLoginPageGeneratingFilter"
class="org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter">
<constructor-arg ref="usernamePasswordAuthenticationFilter" />
<property name="authenticationUrl" value="/login" />
</bean>
<!--Authentication exception handler-->
<bean id="exceptionTranslationFilter"
class="org.springframework.security.web.access.ExceptionTranslationFilter">
<constructor-arg>
<bean class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
<constructor-arg value="/login" />
</bean>
</constructor-arg>
</bean>
<!--Authentication / authorization processing-->
<bean id="filterSecurityInterceptor"
class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="accessDecisionManager" ref="accessDecisionManager"/>
<property name="securityMetadataSource" ref="securityMetadataSource" />
</bean>
<!--Authentication processing body-->
<bean id="authenticationManager"
class="org.springframework.security.authentication.ProviderManager">
<constructor-arg>
<list>
<bean class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
<property name="userDetailsService">
<bean class="org.springframework.security.provisioning.InMemoryUserDetailsManager">
<constructor-arg>
<list>
<bean class="org.springframework.security.core.userdetails.User">
<constructor-arg index="0" value="hoge" />
<constructor-arg index="1" value="HOGE" />
<constructor-arg index="2">
<bean class="org.springframework.security.core.authority.AuthorityUtils"
factory-method="commaSeparatedStringToAuthorityList">
<constructor-arg value="ROLE_USER" />
</bean>
</constructor-arg>
</bean>
</list>
</constructor-arg>
</bean>
</property>
</bean>
</list>
</constructor-arg>
</bean>
<!--Authorization processing body-->
<bean id="accessDecisionManager"
class="org.springframework.security.access.vote.AffirmativeBased">
<constructor-arg>
<list>
<bean class="org.springframework.security.web.access.expression.WebExpressionVoter" />
</list>
</constructor-arg>
</bean>
<!--Definition information such as URLs that require authentication and permission mappings-->
<bean id="securityMetadataSource"
class="org.springframework.security.web.access.expression.ExpressionBasedFilterInvocationSecurityMetadataSource">
<constructor-arg index="0">
<util:map id="requestMap" map-class="java.util.LinkedHashMap">
<entry>
<key>
<bean class="org.springframework.security.web.util.matcher.AntPathRequestMatcher">
<constructor-arg value="/login" />
</bean>
</key>
<bean class="org.springframework.security.access.SecurityConfig"
factory-method="createList">
<constructor-arg value="permitAll" />
</bean>
</entry>
<entry>
<key>
<bean class="org.springframework.security.web.util.matcher.AntPathRequestMatcher">
<constructor-arg value="/**" />
</bean>
</key>
<bean class="org.springframework.security.access.SecurityConfig"
factory-method="createList">
<constructor-arg value="isAuthenticated()" />
</bean>
</entry>
</util:map>
</constructor-arg>
<constructor-arg index="1">
<bean class="org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler" />
</constructor-arg>
</bean>
</beans>
The class diagram looks like the following.
To put it a little more roughly,
Like this.
With FilterChainProxy
as the entrance, various Filter
s grouped by SecurityFilterChain
are executed in order, and FilterSecurityInterceptor
performs authentication / authorization processing just before the protected object.
When I read the Spring Security reference, I often come across the term ** secure object **.
This is a term defined by Spring Security and refers to what is (or should be) secured.
Typical examples are "Web request" and "method execution".
When I first saw the name of FilterSecurityInterceptor
, I was confused as to whether this was Filter
or ʻInterceptor`.
This class implements the Filter
interface, so it looks like Filter
at first glance.
The answer, or rather, when I looked at the type hierarchy of this class, I came to know the true nature of this class.
ʻMethodSecurityInterceptor, which has AbstractSecurityInterceptor
as the same parent, exists as a sibling class.
That is, ʻInterceptor for the security of
Filteris
FilterSecurityInterceptor, Since ʻInterceptor
for the security of method execution is MethodSecurityInterceptor
, these are just ʻInterceptor`.
The FilterSecurityInterceptor
is, so to speak," the Interceptor
for the Filter
by the Filter
of the Filter
". maybe.
Java Configuration Starting with Spring Security 3.2, Java Configuration added in Spring 3.1 is available. Java Configuration allows you to write namespace-like settings without an XML file.
Using Java Configuration has the advantage that the compiler will check for some configuration mistakes and refactoring will be easier.
Folder structure
|-build.gradle
`-src/main/
|-java/
| `-sample/spring/security/
| `-MySpringSecurityConfig.java
`-webapp/
|-index.jsp
`-WEB-INF/
`-web.xml
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextClass</param-name>
<param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
</context-param>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>sample.spring.security.MySpringSecurityConfig</param-value>
</context-param>
</web-app>
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.inMemoryAuthentication()
.withUser("hoge").password("HOGE").roles("USER");
}
}
ʻIndex.jsp` has not changed.
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app ...>
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextClass</param-name>
<param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
</context-param>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>sample.spring.security.MySpringSecurityConfig</param-value>
</context-param>
</web-app>
Change the setting to use ʻAnnotationConfigWebApplicationContext instead of
XmlWebApplicationContext`, which was used by default.
If you define <context-param>
with the name contextClass
, ContextLoaderListener
will use the class specified there as a container.
Furthermore, the configuration class read by ʻAnnotationConfigWebApplicationContext can be specified by defining
with the name
contextConfigLocation`.
MySpringSecurityConfig.java
package sample.spring.security;
...
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@EnableWebSecurity
public class MySpringSecurityConfig extends WebSecurityConfigurerAdapter {
...
}
To enable Java Configuration for Spring Security, annotate the class loaded with ʻAnnotationConfigWebApplicationContext with
@EnableWebSecurity`.
You can see why Spring Security is enabled by looking at the implementation of this annotation.
EnableWebSecurity.java
...
package org.springframework.security.config.annotation.web.configuration;
...
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.security.config.annotation.authentication.configuration.EnableGlobalAuthentication;
...
@Import({ WebSecurityConfiguration.class,
SpringWebMvcImportSelector.class })
@EnableGlobalAuthentication
@Configuration
public @interface EnableWebSecurity {
...
}
WebSecurityConfiguration
is loaded using @ Import
.
This class is also a configuration class annotated with @Configuration
. </ span>
WebSecurityConfiguration.java
...
package org.springframework.security.config.annotation.web.configuration;
...
import javax.servlet.Filter;
...
@Configuration
public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAware {
...
@Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
public Filter springSecurityFilterChain() throws Exception {
...
return webSecurity.build();
}
...
}
ʻThe value of AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME is
"springSecurityFilterChain" . The return value of
webSecurity.build ()is
FilterChainProxy`, and the bean is registered in the same way as it was done in xml.
So, annotate with @EnableWebSecurity
to enable Spring Security.
MySpringSecurityConfig.java
package sample.spring.security;
...
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
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();
}
...
}
To make specific settings for Spring Security, first inherit the class WebSecurityConfigurerAdapter
.
The WebSecurityConfigurerAdapter
is designed to build a simple SecurityFilterChain
by default.
By inheriting this class and overriding methods such as configure (HttpSecurity)
, you can customize the SecurityFilterChain
to be built.
The HttpSecurity
received as an argument corresponds to the<http>
tag in namespace.
Basically, the settings made with <http>
can also be made with HttpSecurity
.
The above implementation has the same meaning as the XML below.
(* Logout is omitted because WebSecurityConfigurerAdapter
is automatically registered internally.)
applicaitonContext.xml
<sec:http>
<sec:intercept-url pattern="/login" access="permitAll" />
<sec:intercept-url pattern="/**" access="isAuthenticated()" />
<sec:form-login />
<sec:logout />
</sec:http>
ʻThe authorizeRequests ()method is the method that initiates the setting of
in the namespace. The return type of the method has been replaced by a class called ʻExpressionInterceptUrlRegistry
, and a dedicated setting method can be called.
If you want to finish the setting of <intercept-url>
and continue another setting, insert the ʻand () method. The ʻand ()
method returns HttpSecurity
, so you can continue with the next configuration without breaking the method chain.
formLogin ()
, as the name implies, enables Form authentication.
MySpringSecurityConfig.java
package sample.spring.security;
...
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
@EnableWebSecurity
public class MySpringSecurityConfig extends WebSecurityConfigurerAdapter {
...
@Autowired
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("hoge").password("HOGE").roles("USER");
}
}
ʻAuthenticationManagerBuilder is a support class for defining the following classes of ʻAuthenticationManager
, and you can define ʻUserDetailsService` etc. using the method chain as in the above implementation.
By the way, you may declare the method that defines the bean directly without using ʻAuthenticationManagerBuilder`.
When setting in Bean definition
package sample.spring.security;
import org.springframework.context.annotation.Bean;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
...
@EnableWebSecurity
public class MySpringSecurityConfig extends WebSecurityConfigurerAdapter {
...
@Bean
public UserDetailsService userDetailsService() {
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager.createUser(User.withUsername("hoge").password("HOGE").roles("USER").build());
return manager;
}
}
Roughly, it seems that it is initialized in the following flow.
doBuild ()
).
If you want to know the exact flow, please follow the actual implementation by referring to the figure below.If your AP server is Servlet 3.0 or above, you can start using Spring Security without web.xml.
Folder structure
|-build.gradle
`-src/main/
|-java/
| `-sample/spring/security/
| |-MySpringSecurityInitializer.java
| `-MySpringSecurityConfig.java
`-web/
`-index.jsp
MySpringSecurityInitializer.java
package sample.spring.security;
import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
public class MySpringSecurityInitializer extends AbstractSecurityWebApplicationInitializer {
public MySpringSecurityInitializer() {
super(MySpringSecurityConfig.class);
}
}
MySpringSecurityConfig.java
and ʻindex.jsp` are the same as the previous ones, so I will omit them.
I've removed web.xml
and added a class called MySpringSecurityInitializer
instead.
This class is created by inheriting from ʻAbstractSecurityWebApplicationInitializer. Then, implement to pass the
Class object of the Java Configuration class (
MySpringSecurityConfig`) to the parent class in the constructor.
This eliminates all the settings that were done in web.xml
.
Several mechanisms added in Servlet 3.0 are used to implement this functionality.
Let's see how the setting is realized without web.xml
.
ServletContainerInitializer
ʻAbstractSecurityWebApplicationInitializer implements an interface called
WebApplicationInitializer. The Javadoc for this
WebApplicationInitializer` states:
WebApplicationInitializer.java
/**
* ...
*
* <p>Implementations of this SPI will be detected automatically by {@link
* SpringServletContainerInitializer}, which itself is bootstrapped automatically
* by any Servlet 3.0 container. See {@linkplain SpringServletContainerInitializer its
* Javadoc} for details on this bootstrapping mechanism.
* ...
*/
public interface WebApplicationInitializer {
It seems that the class that implements this interface is automatically loaded by the class SpringServletContainerInitializer
in the environment of Servlet 3.0 or higher.
Go see the implementation of SpringServletContainerInitializer
.
SpringServletContainerInitializer.java
package org.springframework.web;
...
import javax.servlet.ServletContainerInitializer;
import javax.servlet.annotation.HandlesTypes;
...
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {
...
It implements ServletContainerInitializer
and is annotated with @HandlesTypes
.
Both were added in Servlet 3.0.
When the Servlet container starts, the ʻonStartup (Set <Class <? >>, ServletContext) method of this class is executed. In the first argument of the method, the container automatically searches for the [^ 1] class related to
Class specified by
@HandlesTypesand passes it. In the case of
SpringServletContainerInitializer,
WebApplicationInitializer` is specified, so the class that implements this interface is searched by the Servlet container and passed.
[^ 1]: Implementation class for interface, its class and subclass for abstract / concrete class, and annotated class for annotation.
When the ʻonStartup ()method of
SpringServletContainerInitializer is executed, an instance of the class that implements
WebApplicationInitializeris created and the ʻonStartup (ServletContext)
method of WebApplicationInitializer
is executed.
AbstractSecurityWebApplicationInitializer
The ʻonStartup (ServletContext) method of ʻAbstractSecurityWebApplicationInitializer
has the following implementation.
AbstractSecurityWebApplicationInitializer.java
package org.springframework.security.web.context;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.Set;
import javax.servlet.Filter;
import javax.servlet.ServletContext;
...
import org.springframework.context.ApplicationContext;
...
import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.AbstractContextLoaderInitializer;
import org.springframework.web.context.ContextLoaderListener;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.filter.DelegatingFilterProxy;
...
public abstract class AbstractSecurityWebApplicationInitializer
implements WebApplicationInitializer {
...
public static final String DEFAULT_FILTER_NAME = "springSecurityFilterChain";
...
public final void onStartup(ServletContext servletContext) throws ServletException {
beforeSpringSecurityFilterChain(servletContext);
if (this.configurationClasses != null) {
AnnotationConfigWebApplicationContext rootAppContext = new AnnotationConfigWebApplicationContext(); ★
rootAppContext.register(this.configurationClasses);
servletContext.addListener(new ContextLoaderListener(rootAppContext)); ★
}
if (enableHttpSessionEventPublisher()) {
servletContext.addListener(
"org.springframework.security.web.session.HttpSessionEventPublisher");
}
servletContext.setSessionTrackingModes(getSessionTrackingModes());
insertSpringSecurityFilterChain(servletContext);
afterSpringSecurityFilterChain(servletContext);
}
...
private void insertSpringSecurityFilterChain(ServletContext servletContext) {
String filterName = DEFAULT_FILTER_NAME;
DelegatingFilterProxy springSecurityFilterChain = new DelegatingFilterProxy(
filterName); ★
String contextAttribute = getWebApplicationContextAttribute();
if (contextAttribute != null) {
springSecurityFilterChain.setContextAttribute(contextAttribute);
}
registerFilter(servletContext, true, filterName, springSecurityFilterChain);
}
...
}
There are some familiar classes here and there ...
In short, the settings that were done in web.xml
are done in this implementation.
It is initialized by the following flow. (As usual, there are some parts that are not strict for simplification)
Recommended Posts