[JAVA] How Spring Security works with Hello World

Self-introduction


What to talk about today

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


background

--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?


Abstract settings

--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.


Target person

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

Prerequisite knowledge

--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


Hello World App (Premise)

Don't use Spring Boot

--The purpose is to learn the basics of Spring Security --Use only plain Spring Security

Do not use Java Config

--Write the settings in xml ――After all, Java Config is the same

Do not use Servlet 3.0 features

--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.


Hello World app (environment construction)

Source

https://github.com/opengl-8080/spring-security-hello-world

Operation check environment

  • Java 1.8.0_121
  • Tomcat 8.0.35

Get the war file

-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.

Operation check

--Deploy spring-security.war to Tomcat --Access http: // localhost: 8080 / spring-security


Hello World app (operation)

Go to http: // localhost: 8080 / spring-security

spring-security.jpg

Log in by entering foo in User and Password

spring-security.jpg

Get a 403 error

Next, log in with bar

spring-security.jpg

spring-security.jpg

The index page is displayed.

You can log out by clicking the logout button.


Hello World App (Implementation)

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>

Read Hello World

  1. Start server-accept request
  2. Spring Security entrance
  3. Login process
  4. Access control
  5. Logout process

Read Hello World

  1. ** Start server-Receive request **
  2. Spring Security entrance
  3. Login process
  4. Access control
  5. Logout process

Spring container initialization

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 file

Registration of DelegatingFilterProxy

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>

--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


Role of DelegatingFilterProxy

DelegatingFilterProxyの役割.png

--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.


Spring Security settings

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 liver

applicationContext.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


Summary (Start server-Receive request) </ span>

test

  1. The Servlet container is started and the ContextLoaderListener registered as Listener in web.xml is executed.
  2. An instance of XmlWebApplicationContext is created and /WEB-INF/applicationContext.xml is loaded.
  3. The <http> tag registers an instance of FilterChainProxy with the Spring container with the name "" springSecurityFilterChain ".
  4. The DelegateFilterProxy registered as Filter in web.xml is generated by the Servlet container.
  5. When requested, DelegateFilterProxy is called to get the bean from the Spring container with its own name (" springSecurityFilterChain ") (FilterChainProxy is obtained).
  6. Processing is delegated to FilterChainProxy.
  • Some parts may not be strictly correct due to the priority given to clarity. Please look at the atmosphere.

Read Hello World

  1. Start server-Receive request </ del>
  2. ** Spring Security entrance **
  3. Login process
  4. Access control
  5. Logout process

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 and Filter

SpringSecurityとFilter.jpg

--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 for each URL pattern

patternごとのSecurityFilterChain.jpg

--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)


Summary (Spring Security entrance) </ span>

--<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

patternごとのSecurityFilterChain.jpg


Read Hello World

  1. Start server-Receive request </ del>
  2. Spring Security entrance </ del>
  3. ** Login process **
  4. Access control
  5. Logout process

Enable Form login

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

Default login page

spring-security.jpg

--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.


Handling login requests

--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.

AuthenticationManagerに委譲.png

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.

ProviderManagerとAuthenticationProvider.png


ProviderManager and AuthenticationProvider

AuthenticationProviderの実装クラス達.png

--ʻ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`).


AuthenticationProvider for password authentication

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に委譲.png


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


Use InMemoryUserDetailsManager

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.


Use User

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.


Organize the flow so far

ログイン処理整理.png

  1. When a POST request comes to / login, ʻUsernamePasswordAuthenticationFilter` performs the authentication process.
  2. Authentication processing is delegated to ʻAuthenticationManager`
  3. ProviderManager, which is an implementation class of ʻAuthenticationManager, delegates the authentication process to its own ʻAuthenticationProvider.
  4. DaoAuthenticationProvider registered with the<authentication-provider>tag performs authentication processing based on the user name and password.
  5. At that time, the search for user information is delegated to ʻUserDetailsService`.
  6. ʻInMemoryUserDetailsManager 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>

If authentication is successful

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.


Summary (Login process) </ span>

--<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.


Read Hello World

  1. Start server-Receive request </ del>
  2. Spring Security entrance </ del>
  3. Login process </ del>
  4. ** Access control **
  5. Logout process

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

Secure object

-** 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.


Delegation of access control

AccessDecisionManagerに委譲.png

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


Access control by voting

投票によるアクセス制御

--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 implementation class

AccessDecisionManagerと実装クラス.png

--ʻAccessDecisionManager` has three implementation classes

  • AffirmativeBased --Accessable if there is even one "grant" --This class is used by default
  • ConsensusBased --Accessable if "Reject" <"Give"
  • UnanimousBased --Accessible if all are "granted"

Expression-based access control

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


Summary (access control) </ span>

--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.


Read Hello World

  1. Start server-Receive request </ del>
  2. Spring Security entrance </ del>
  3. Login process </ del>
  4. Access control </ del>
  5. ** Logout process **

Enable logout

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.


Looking back on the whole

Server startup-request reception

test

Spring Security entrance

SpringSecurityとFilter.jpg

Login process

ログイン処理整理.png

Access control

投票によるアクセス制御


This is the only meaning in this setting

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>

How was it?
Spring Security Did you think it was easy? </ span>


[Sad news] Still Hello World


Features and elements that I'm not talking about today

  • GrantedAuthority --Role layering --Method security
  • CSRF
  • Memember-Me --Password encoding --Various customization methods --Test
  • etc...

But if today's story is a foundation of knowledge,
it should be overcome (probably) </ span>


that's all

Recommended Posts