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 the
tag --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 with
xmlns: 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 Filter
s 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 multipleFilter
s
--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 Filter
s 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 by
WebExpressionVoter` 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