Getting started with Spring Security
1, Use of Spring Security
1.1 basic terms
Spring Security is a powerful and highly customizable authentication and authorization framework. It is a set of Web security standards for spring applications. Spring Security focuses on providing authentication and authorization functions for java applications.
OAuth2 is an industry standard protocol for authorization, which provides class specific authorization flow to simplify client development.
Resource owner: the end user who owns the resource and has the account and password to access the resource.
Resource server: the server that owns the protected resource. If the request contains the correct access token, it can access the resource.
Client: the client that accesses the resources will use the access token to obtain the resources of the resource server, which can be a browser, mobile device or server.
Authorization server: a server used to authorize users. If the client passes the authorization, it issues a token to access the resource server.
Four authorization modes:
-
Authorization Code mode: the client first directs the user to the authorization server, logs in and obtains the Authorization Code, then authorizes, and finally obtains the access token according to the Authorization Code.
-
Implicit (simplified mode): compared with the authorization code mode, it cancels the process of obtaining the authorization code and directly obtains the access token.
-
Resource Owner Password Credential (password mode): the client directly obtains the user name and password from the user, and then obtains the access token from the authorization server.
-
Client Credential: the client obtains the access token from the authorization server directly through the client authorization.
1.2 basic use
1.2.1 import dependency
Create a new module tools security and introduce dependencies:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- Safety certification --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency>
1.2.2 configuration file
server: port: 9040 spring: application: name: tools-security
1.2.3 test interface
@RequestMapping("test") public String test() { return "test"; }
Run the project and find the Using generated security password in some logs intercepted below. The following string is the original password (the results are different each time) and the account is user
2021-10-23 15:52:58.472 WARN [tools-security,,] 100588 --- [ restartedMain] o.s.c.s.a.z.ZipkinAutoConfiguration : Check result of the [RestTemplateSender{http://localhost:9411/api/v2/spans}] contains an error [CheckResult{ok=false, error=org.springframework.web.client.HttpClientErrorException$BadRequest: 400 Bad Request: [Empty JSON_V2 message]}] 2021-10-23 15:52:59.049 INFO [tools-security,,] 100588 --- [ restartedMain] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor' 2021-10-23 15:52:59.303 INFO [tools-security,,] 100588 --- [ restartedMain] .s.s.UserDetailsServiceAutoConfiguration : Using generated security password: 0c731ade-806a-4a8e-83e1-e11044fe0f48
visit http://localhost:9040/test , you will jump to the default login page and enter the account (user) password (0c731ade-806a-4a8e-83e1-e11044fe0f48) to access the / test interface.
1.3 15 filters in the filter chain
1. org.springframework.security.web.context.SecurityContextPersistenceFilter
The first filter to bear the brunt
The SecurityContextPersistenceFilter mainly uses the SecurityContextRepository to save or update a securitycontext in the session and give the securitycontext to future filters to establish the required context for subsequent filters.
2. org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter
This filter is used to integrate SecurityContext into WebAsyncManager in Spring asynchronous execution mechanism
3. org.springframework.security.web.header.HeaderWriterFilter
Add corresponding information to the requested Header, which can be controlled by using security: headers inside the http tag
4. org.springframework.security.web.csrf.CsrfFilter
CSRF is also called cross domain request forgery. Spring security will verify whether all post requests contain the token information of CSRF generated by the system. If not, an error will be reported. It can prevent CSRF attack.
5. org.springframework.security.web.authentication.logout.LogoutFilter
Match the request with the URL of / logout to exit the user and clear the authentication information.
6. org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter
The authentication operation depends on this filter. The default matching URL is / login and must be a post request.
7. org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter
If no authentication page is specified in the configuration file, a default authentication page is generated by the filter
8. org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter
This filter can produce a default exit login page
9. org.springframework.security.web.authentication.www.BasicAuthenticationFilter
This filter will automatically parse the header information in the http request whose header name is Authentication and starts with Basic.
10. org.springframework.security.web.savedrequest.RequestCacheAwareFilter
A RequestCache is maintained internally through HttpSessionRequestCache to cache HttpServletRequest
11. org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter
The ServletRequest is wrapped once, so that the request has a richer API
12. org.springframework.security.web.authentication.AnpnymousAuthenticationFilter
When the authentication information in the SecurityContextHolder is empty, an anonymous user will be created and stored in the SecurityContextHolder.
In order to be compatible with unlisted access, spring security also goes through a set of authentication process, which is just an anonymous identity.
13. org.springframework.security.web.session.SessionManagementFilter
The SecurityContextRepository limits the number of replies opened by the same user
14. org.springframework.security.web.access.ExceptionTranslationFilter
The exception transfer filter is located behind the entire spring securityfilterchain and is used to convert exceptions in the entire link.
15. org.springframework.security.web.access.intercept.FilterSecurityinterceptor
Obtain the authorization information of the configured resource access, and determine whether it has permission according to the user information stored in the SecurityContextHolder
1.4 user defined authentication WebSecurityConfigurerAdapter
The previous method is to use the default account user and the password automatically generated when the application is started for access authentication. However, each system should have its own user system. User login authentication and interface authentication control need to be customized. Here, websecurity configuration needs to be configured, mainly focusing on three methods: configure(HttpSecurity http), configure(AuthenticationManagerBuilder auth) and configure(WebSecurity).
- HttpSecurity allows network-based security to be configured at the resource level based on selection matching, and declares that any other web address needs to be successfully verified. That is to restrict the access path of the role.
- AuthenticationManagerBuilder is used to establish authentication mechanisms by allowing authenticationproviders to be easily added. The configured authentication information is mainly used to configure user identity and role information, that is, it is used to record account, password and role information.
- WebSecurity is used to configure settings that affect global security (configure resources, set debugging mode, and reject requests by implementing custom firewall definitions). It is generally used to configure some general things of the global, such as static resources.
Create WebSecurityConfiguration.java and inherit WebSecurityConfigurerAdapter
@Configuration public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter { }
1.4.1 configure(HttpSecurity http)
HttpSecurity has two important methods: authorizeRequests() and requestMatchers()
authorizeRequests()
Methods of authorization management control
This method returns an expressionurlauthorizationconfigurator.expressionintercepturlregistry object.
authorizeRequests defines those URLs that need to be protected and those that do not need to be protected. It usually comes out in the first line of the configuration.
All Security permissions are controlled based on this class. For example:
http.authorizeRequests().anyRequest().authenticated() requires permission authentication for all interfaces, which is equivalent to http.authorizeRequests().antMatchers("/**").authenticated();
The http.authorizeRequests().antMatchers("/**").permitAll() configuration requires that all interfaces do not need permission authentication, which is equivalent to http.authorizeRequests().anyRequest().permitAll()
In addition, the antMatchers method in these two codes is to configure matching rules. That is, which interfaces need or do not need permission authentication.
anyRequest(): all interfaces
authenticated(): authority authentication is required
permitAll(): permission authentication is not required
Ant matchers (string): matching interface
requestMatchers()
Obtain the RequestMatcherConfigurer object and configure the routes that can be filtered; For example, requestMatchers().anyRequest() is equivalent to http.authorizeRequests().anyRequest().access("permitAll");
give an example:
package com.lmc.security.conf; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; /** * @author lmc * @Description: TODO * @Create 2021-10-23 19:55 * @version: 1.0 */ @Configuration public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/403", "/404", "/500").permitAll() // Authentication is not required when accessing 403 / 404 / 500 .anyRequest().authenticated() // Other requests require authentication .and() .formLogin() // Login via form .and() .csrf().disable(); // Turn off Cross Site Request Forgery protection } }
In the above configuration, I have defined three interfaces with requests of / 403, / 404 and / 500. They do not need to be authenticated and can be released directly; Then other requests need authentication; Then, the form mode should be used when logging in, and the Cross Site Request Forgery protection should be turned off at the same time.
Before configuring, I have created three pages 403 / 404 / 500
@Controller public class PageController { @RequestMapping("403") public String accessError() { return "403"; } @RequestMapping("404") public String notFound() { return "404"; } @RequestMapping("500") public String serverError() { return "500"; } }
At this time, run the project and access the / 404 interface normally. Then, when accessing the / test interface, you will jump to the default login page of Spring Security and enter the account and password before release.
1.4.2 configure(AuthenticationManagerBuilder auth)
Login with custom user password (memory mode)
@Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth .inMemoryAuthentication() // Store user information in memory .withUser("lmc").password("{noop}123") // The user name is lmc and the password is 123, {noop} means that the password is not encrypted .roles("USER"); // Give the USER lmc the USER role }
The default password used by spring security needs to be encrypted, that is, the password in. password(String) is already an encrypted string. If {noop} is added before it, it means an unencrypted password
.withUser("lmc").password("{noop}123") It can also be written as .passwordEncoder(new BCryptPasswordEncoder()).withUser("lmc").password(new BCryptPasswordEncoder().encode("123"))
Note that before security 5.0, the default encryption method was BC, so you only need to write
.withUser("lmc").password(new BCryptPasswordEncoder().encode("123"))
Yes, but the encryption method should be defined after 5.0, so you need to use the passwordEncoder() method to define the encryption method, otherwise the following exceptions will be reported:
There is no PasswordEncoder mapped for the id "null"
1.4.3 configure(WebSecurity web)
Configure static resources and ignore authentication
@Override public void configure(WebSecurity web) throws Exception { web.ignoring() .antMatchers("/assets/**") .antMatchers("/403", "/404", "/500") .antMatchers("/login.html"); }
In fact, the content of configure(WebSecurity web) can be basically configured in configure(HttpSecurity http). For example, the above configuration is configured in configure(HttpSecurity http) as follows:
@Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/403", "/404", "/500").permitAll() // Authentication is not required when accessing 403 / 404 / 500 .antMatchers("/assets/**").permitAll() .antMatchers("/login.html").permitAll(); }
However, we generally configure common things such as login pages, error pages and static resources in configure(WebSecurity web). configure(HttpSecurity http) prefers network-based security, that is, the control of role permissions, which is clear-cut and creates better observability.
1.4.4 user defined form login
We often don't use the default login page of security. We need to create and develop our own custom login page, which can be operated through HttpSecurity.
Customize the login page first:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Login Page</title> <!-- Import style --> <link rel="stylesheet" href="/assets/element-ui/index.css"> </head> <style> .el-header { background-color: #1B73E8; color: white; text-align: left; line-height: 60px; } .el-footer { background-color: #F5F5F5; color: black; text-align: center; line-height: 60px; font-size: 12px; } .el-main { background-color: white; color: #333; text-align: center; line-height: 160px; } body > .el-container { margin-bottom: 40px; } .el-row { margin-bottom: 20px; } .el-col { border-radius: 4px; } .bg-purple-dark { background: #99a9bf; } .bg-purple { background: #d3dce6; } .bg-purple-light { /*background: #e5e9f2;*/ } .grid-content { border-radius: 4px; min-height: 36px; height: 800px; } .row-bg { padding: 10px 0; background-color: #f9fafc; } .el-card { padding: 80px; height: 638px; } </style> <body> <div id="app"> <el-container> <el-header>lmc-tools</el-header> <el-main> <el-row :gutter="20"> <el-col :span="7"><div class="grid-content bg-purple"></div></el-col> <el-col :span="10"> <div class="grid-content bg-purple-light"> <el-card class="box-card"> <el-form ref="form" action="/login" method="post" :model="form" :rules="rules" label-width="100px" name="loginform" class="login-form"> <el-form-item label="user name" prop="username"> <el-input v-model="form.username" name="username" clearable></el-input> </el-form-item> <el-form-item label="password" prop="password"> <el-input v-model="form.password" name="password" show-password type="password" clearable></el-input> </el-form-item> <el-form-item> <el-button type="primary" @click="submitForm('form')">Sign in</el-button> </el-form-item> </el-form> </el-card> </div> </el-col> <el-col :span="7"><div class="grid-content bg-purple"></div></el-col> </el-row> </el-main> <el-footer>Copyright © 2021 lmc</el-footer> </el-container> </div> </body> <!-- introduce vue --> <script src="/assets/vue/vue.js"></script> <!-- introduce Element-UI Component library --> <script src="/assets/element-ui/index.js"></script> <script> new Vue({ el: "#app", data: { form: { username: '', password: '' }, rules: { username: [ {required: true, message: 'User name cannot be empty', trigger: 'blur'} ], password: [ {required: true, message: 'Password cannot be empty', trigger: 'blur'} ] } }, methods: { // Event triggered by login button submitForm: function(formName) { // Verify that the input box is empty this.$refs[formName].validate((valid) => { if (valid) { document.loginform.submit() } }) } } }) </script> </html>
Define the interface to access the login page:
/** * Login page * @return */ @RequestMapping("login.html") public String login() { return "login"; }
Configuration class modification:
package com.lmc.security.conf; import org.springframework.context.annotation.Configuration; 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.builders.WebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; /** * @author lmc * @Description: TODO * @Create 2021-10-23 19:55 * @version: 1.0 */ @Configuration public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter { @Override public void configure(WebSecurity web) throws Exception { web.ignoring() .antMatchers("/assets/**") .antMatchers("/403", "/404", "/500") .antMatchers("/login.html"); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth .inMemoryAuthentication() // Store user information in memory .withUser("lmc").password("{noop}123") // The user name is lmc and the password is 123, {noop} means that the password is not encrypted .roles("USER"); // Give the USER lmc the USER role } @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .anyRequest().authenticated() // Other requests require authentication .and() .formLogin() // Login via form .loginPage("/login.html") .loginProcessingUrl("/login") .failureUrl("/500") .and() .csrf().disable(); // Turn off Cross Site Request Forgery protection } }
At this point, access http://localhost:9040/test Will jump to http://localhost:9040/login.html Page, if the account password is entered incorrectly, it will jump to the failure page http://localhost:9040/500 , custom login completed.