Explain the basics of how Spring Security works through a brief Hello World
――What kind of classes are working together and how ――How the configuration file is related
--Personally started studying Spring Security ――I tried to write Hello World for a moment, but it was difficult ――Why is this setting necessary? ―― Why is ~~~ effective when you write ...? ――Is this setting necessary for the minimum Hello World?
--Spring Security settings are highly abstracted ――While the settings are simple, it is difficult to understand what is going on behind the scenes. ――It becomes difficult to understand the mechanism and customize it.
By learning how Spring Security works through Hello World
-- (so that you can become) </ font> for reference for those who are studying Spring Security -- (so that you can become) </ font> for reference of those who used Spring Security without knowing how it works
--Has a decent knowledge of Servlets and JSPs
--Knowing Filter etc., you can write web.xml to build a Servlet app
--Has a decent knowledge of Spring
--Understanding beans and AOP what a DI container is
--The purpose is to learn the basics of Spring Security --Use only plain Spring Security
--Write the settings in xml ――After all, Java Config is the same
--If you use the function of Servlet 3.0, you can set it without web.xml. ――The purpose is to learn the basics of the mechanism, so don't use it either.
https://github.com/opengl-8080/spring-security-hello-world
-Download spring-security.war from here
--If you are building from source, download the source and run gradlew war at the root of your project.
--Deploy spring-security.war to Tomcat
--Access http: // localhost: 8080 / spring-security
Go to http: // localhost: 8080 / spring-security

Log in by entering foo in User and Password

Get a 403 error
Next, log in with bar


The index page is displayed.
You can log out by clicking the logout button.
file organization
|-build.gradle :Gradle build file
|
`-src/main/webapp/
|
|-index.jsp :Index page
|
`-WEB-INF/
|
|-applicationContext.xml :Spring configuration file
|
`-web.xml :Servlet configuration file
--All 4 files ――One is a build file, so three files are actually deployed. --Ultra-simple configuration without any Java source
build.gradle
apply plugin: 'war'
sourceCompatibility = '1.8'
targetCompatibility = '1.8'
compileJava.options.encoding = 'UTF-8'
repositories {
mavenCentral()
}
dependencies {
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'
task wrapper(type: Wrapper) {
gradleVersion = '3.2.1'
}
index.jsp
<%@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>
<form action="logout" method="post">
<input type="submit"
value="logout" />
<input type="hidden"
name="${_csrf.parameterName}"
value="${_csrf.token}" />
</form>
</body>
</html>
--Top page displayed after login
--Just a message and a logout button
-- _csrf is set because it is enabled by default, but since it will not appear in the following explanation, detailed explanation will be omitted.
--For details, please refer to Spring Security Usage Memo CSRF | Qiita.
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">
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<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>
</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() and hasAuthority('BAR')" />
<sec:form-login />
<sec:logout />
</sec:http>
<sec:authentication-manager>
<sec:authentication-provider>
<sec:user-service>
<sec:user name="foo"
password="foo"
authorities="" />
<sec:user name="bar"
password="bar"
authorities="BAR" />
</sec:user-service>
</sec:authentication-provider>
</sec:authentication-manager>
</beans>
web.xml
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<listener>
--The Servlet function allows you to define the process to be executed when the application starts.ContextLoaderListener
--Class that initializes Spring container
--By default XmlWebApplicationContext is used
--WEB-INF / applicationContext.xml is used as the configuration fileweb.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>
--Register a Filter named DelegatingFilterProxy with the name springSecurityFilterChain
--Filter is a function provided by Servlet
--Arbitrary processing can be inserted before and after the request
--Specify / * for ʻurl-pattern` to process all requests

--DelegatingFilterProxy searches the Spring container for beans that match the following conditions:
--The bean name matches its Filter name (springSecurityFilterChain)
--Implementing the javax.servlet.Filter interface
--Delegate processing to the acquired bean
--Since the class to which DelegatingFilterProxy delegated the processing is a bean obtained from the Spring container, you can use the functions of the Spring container.
- DI
- AOP
- etc...
--The role of DelegatingFilterProxy is to bridge the Servlet container and Spring container.
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() and hasAuthority('BAR')" />
<sec:form-login />
<sec:logout />
</sec:http>
...
--ʻApplicationContext.xmlis a Spring configuration file --Define a bean using thetag --Not a configuration file dedicated to Spring Security --A dedicated tag is provided to allow you to write Spring Security settings concisely. --Called ** namespace ** in the reference --Reading withxmlns: sec`
<http> is the liverapplicationContext.xml
<sec:http>
...
</sec:http>
--This <http> is the key to setting Spring Security.
--A large number of important beans are registered
FilterChainProxy
--One of the beans registered by the <http> tag
--Inheriting the Filter interface
--Registered in the Spring container with the name springSecurityFilterChain
--The identity of the bean delegated by DelegatingFilterProxy

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.SecurityFilterChain
--One of the important classes that <http> registers
--As the name suggests, Filter is chained.
--FilterChainProxy delegates the received request to the Filters of SecurityFilterChain
--Example of Filter registered by default
- SecurityContextPersistenceFilter
- CsrfFilter
- AnonymousAuthenticationFilter
- ExceptionTranslationFilter
- FilterSecurityInterceptor
- etc...

--Spring Security is realized by a combination of Filter
--A Filter is prepared for each function, and you can enable / disable the function by registering the Filter in the SecurityFilterChain.

--SecurityFilterChain can be defined for each URL pattern
--For access to URLs that match / api / **, use SecurityFilterChain set for the REST API.
--For access to other URLs (/ **), use SecurityFilterChain set for normal screen access.
--Can be said
--For example, functions such as "Form login" are no longer needed when accessing with REST API.
The configuration file looks like this:
applicationContext.xml
<sec:http pattern="/api/**">
...
</sec:http>
<sec:http pattern="/**">
...
</sec:http>
--Specify in the pattern attribute of the<http>tag
(Can be specified in Ant format)
--The settings are applied in order from the top, so bring the one with more limited settings to the top.
(If the / ** setting is above / api / **, access to / api / ** will match the setting of / ** first)
--<http> registers SecurityFilterChain as a bean
--SecurityFilterChain holds multipleFilters
--Spring Security provides Filter for each function
--By combining Filter, you can define SecurityFilterChain with only the necessary functions.
--Since SecurityFilterChain can be defined for each URL pattern,
--SecurityFilterChain for REST API
--SecurityFilterChain for normal screen access
Can be set

applicationContext.xml
<sec:http>
...
<sec:form-login />
...
</sec:http>
--Adding the <form-login> tag enables Form login
--The Filter required for Form login is added to SecurityFilterChain

--If the login-page attribute of<form-login>is not specified, aFilter called DefaultLoginPageGeneratingFilter is registered.
--Generates and returns a simple login page when there is a GET method request in / login
--Convenient when you want to try the operation check quickly
--A checkbox for Remember-Me is automatically added when Remember-Me authentication is enabled.
--Login processing is performed by Filter called ʻUsernamePasswordAuthenticationFilter --When a request by the POST method comes to/ login, the authentication process is started. --However, the actual authentication process is delegated to ʻAuthenticationManager.

Please note that a lot of "delegation" comes out from here and it is confusing! </ span>
AuthenticationManager
applicationContext.xml
<sec:authentication-manager> ★
<sec:authentication-provider>
<sec:user-service>
<sec:user name="foo" ... />
<sec:user name="bar" ... />
</sec:user-service>
</sec:authentication-provider>
</sec:authentication-manager>
--By declaring the <authentication-manager> tag, ProviderManager, which is an implementation class of ʻAuthenticationManager, is registered in the Spring container. --Class that is the entrance to authentication processing --However, ProviderManager itself does not perform authentication processing, and ** delegates ** to ʻAuthenticationProvider.


--ʻAuthenticationProvider has many implementation classes for each type of authentication --ProviderManager holds multiple instances of ʻAuthenticationProvider according to the authentication method supported by the application.
--Each ʻAuthenticationProvider is made to judge whether the current authentication request is supported, and if it is supported, the authentication process is performed by ʻAuthenticationProvider.
--ProviderManager is responsible for managing multiple ʻAuthenticationProviders together (exactly ProviderManager`).
applicationContext.xml
<sec:authentication-manager>
<sec:authentication-provider> ★
<sec:user-service>
<sec:user name="foo" ... />
<sec:user name="bar" ... />
</sec:user-service>
</sec:authentication-provider>
</sec:authentication-manager>
--By using the <authentication-provider> tag, a class called DaoAuthenticationProvider is registered as an implementation of ʻAuthenticationProvider. --DaoAuthenticationProvider performs authentication processing using a combination of user name and password. --At that time, use Dao (Data Access Object) to get user information. --The part corresponding to Dao is ** delegated ** to ʻUserDetailsService.

UserDetailsService
UserDetailsService
public interface UserDetailsService {
UserDetails
loadUserByUsername(String username)
throws UsernameNotFoundException;
}
--Has a method that returns user information (ʻUserDetails) based on the user name --Throw ʻUsernameNotFoundException if no user is found
--Spring Security provides several classes that implement this interface
InMemoryUserDetailsManager Implementation that saves user information in memory
JdbcUserDetailsManager Implementation that retrieves user information from the database via JDBC
applicationContext.xml
<sec:authentication-manager>
<sec:authentication-provider>
<sec:user-service> ★
<sec:user name="foo" ... />
<sec:user name="bar" ... />
</sec:user-service>
</sec:authentication-provider>
</sec:authentication-manager>
--Using the <user-service> tag causes ʻInMemoryUserDetailsManager to be registered in the Spring container as an implementation of ʻUserDetailsService.
UserDetails
UserDetails.java
public interface UserDetails extends Serializable {
String getUsername();
String getPassword();
Collection<? extends GrantedAuthority>
getAuthorities();
boolean isAccountNonExpired();
boolean isAccountNonLocked();
boolean isCredentialsNonExpired();
boolean isEnabled();
}
--ʻUserDetails is an interface that provides detailed information about logged-in users. --You can get the status such as user name and password, collection of granted privileges, expired and locked. --A class called ʻUser is prepared as a class that implements this interface.
applicationContext.xml
<sec:authentication-manager>
<sec:authentication-provider>
<sec:user-service>
<sec:user name="foo" ... /> ★
<sec:user name="bar" ... /> ★
</sec:user-service>
</sec:authentication-provider>
</sec:authentication-manager>
--When using the <user> tag, user information is generated using the ʻUserclass. --The generated user information is stored in, that is, ʻInMemoryUserDetailsManager.

/ login, ʻUsernamePasswordAuthenticationFilter` performs the authentication process.ProviderManager, which is an implementation class of ʻAuthenticationManager, delegates the authentication process to its own ʻAuthenticationProvider.DaoAuthenticationProvider registered with the<authentication-provider>tag performs authentication processing based on the user name and password. registered with the tag stores the user information (ʻUserDetails) defined with the<user>tag in memory.The above relationships are included in the settings below.
applicationContext.xml
<sec:authentication-manager>
<sec:authentication-provider>
<sec:user-service>
<sec:user name="foo" ... />
<sec:user name="bar" ... />
</sec:user-service>
</sec:authentication-provider>
</sec:authentication-manager>
AuthenticationProvider.java
public interface AuthenticationProvider {
Authentication authenticate(Authentication authentication)
throws AuthenticationException;
}
--If the authentication process of ʻAuthenticationProvider is successful, the ʻAuthentication object that holds the information of the logged-in user is returned.
--This ʻAuthentication` object is saved in the session and will be referenced in future requests etc.
--<form-login> enables Form authentication
--ʻUsernamePasswordAuthenticationFilter is added and the authentication process starts. --ʻAuthenticationManager controls the authentication process
--ʻAuthenticationProvider performs a specific authentication process --ʻUserDetailsService retrieves user information Dao
--ʻUserDetails provides detailed information about the user --If authentication is successful, the ʻAuthentication object is saved in the session.
FilterSecurityInterceptor
--One of the important Filters added by defining the<http>tag
-Insert processing before and after executing ** secure object ** processing
--Access control for HTTP requests is initiated in this FilterSecurityInterceptor
-** Security protection target ** is called ** Secure Object ** in the Spring Security reference. --Although it says "Object", it is used to mean the original Object "object" rather than a specific Java object (probably). --"HTTP request to a specific URL" or "execute method" is a secure object.

--FilterSecurityInterceptor itself does not check access control
--Access control judgment is delegated to ʻAccessDecisionManager`

--The implementation class of ʻAccessDecisionManager provided by Spring Security controls access by "** voting **". --ʻAccessDecisionManager lets ʻAccessDecisionVoter vote for access -<Font color = "blue"> Grant: Accessable </ font> -<font color = "red"> Denied: Inaccessible </ font> -<font color = "gray"> Abstention: Not supported </ font> --ʻAccessDecisionManager aggregates voting results and draws conclusions
--If yes, do nothing
--If not possible, throw ʻAccessDeniedException`

--ʻAccessDecisionManager` has three implementation classes
AffirmativeBased
--Accessable if there is even one "grant"
--This class is used by defaultConsensusBased
--Accessable if "Reject" <"Give"UnanimousBased
--Accessible if all are "granted"applicationContext.xml
<sec:http>
<sec:intercept-url
pattern="/login"
access="permitAll" />
<sec:intercept-url
pattern="/**"
access="isAuthenticated() and hasAuthority('BAR')" />
...
</sec:http>
--ʻWebExpressionVoter is used by default to implement AccessDecisionVoter
--ʻExpression That is, control accessibility based on expressions --The access control expression is specified by the ʻaccess attribute of the<intercept-url>tag.
--The pattern attribute specifies the pattern of the URL to which the control applies.
Spring Expression Language (SpEL)
applicationContext.xml
access="permitAll"
access="isAuthenticated() and hasAuthority('BAR')"
--Use Spring's own expression language called Spring Expression Language for access control expressions
--Make sure that the result of evaluating the expression is boolean
--If true, accessible </ font>
--If false, inaccessible </ font>
--Functions and constants have been extended for Spring Security
--permitAll: Always true
--ʻIsAuthenticated () : true if authenticated --hasAuthority (): true` if you have the specified privileges
--Access control is started with FilterSecurityInterceptor
--Actual control is done by ʻAccessDecisionManager --The standard implementation uses "voting" to determine access. --ʻAccessDecisionVoter votes, and ʻAccessDecisionManageraggregates the voting results and draws a conclusion. --By default, expression-based access control using SpEL byWebExpressionVoter` is enabled.
applicationContext.xml
<sec:http>
...
<sec:logout />
</sec:http>
--Using the <logout> tag adds a LogoutFilter
--When a POST request comes to / logout, LogoutFilter handles the logout.




web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app ...>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<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>
</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"
...>
<sec:http>
<sec:intercept-url
pattern="/login"
access="permitAll" />
<sec:intercept-url
pattern="/**"
access="isAuthenticated() and hasAuthority('BAR')" />
<sec:form-login />
<sec:logout />
</sec:http>
<sec:authentication-manager>
<sec:authentication-provider>
<sec:user-service>
<sec:user name="foo"
password="foo"
authorities="" />
<sec:user name="bar"
password="bar"
authorities="BAR" />
</sec:user-service>
</sec:authentication-provider>
</sec:authentication-manager>
</beans>
Recommended Posts