Introduction to spring security (based on crazy God)

Keywords: Java Spring RESTful

Safety profile

Security has always been a very important aspect in Web development. Although security is a non functional requirement of an application, it should be considered at the early stage of application development. If the security problem is only considered in the later stage of application development, it may fall into a dilemma: on the one hand, there are serious security vulnerabilities in the application, which can not meet the requirements of users, and may cause users' private data to be stolen by attackers; On the other hand, the basic architecture of the application has been determined. To repair security vulnerabilities, it may be necessary to make major adjustments to the system architecture, so it requires more development time and affects the release process of the application. Therefore, security related factors should be taken into account from the first day of application development, and in the whole application development process.

There are some famous ones on the market: Shiro, Spring Security!

What needs to be explained here is that each framework appears to solve a certain problem. What problem does the Spring Security framework appear to solve?

First, let's take a look at its official website: Spring Security official website address

Spring Security is a powerful and highly customizable authentication and access-control framework. It is the de-facto standard for securing Spring-based applications.

Spring Security is a framework that focuses on providing both authentication and authorization to Java applications. Like all Spring projects, the real power of Spring Security is found in how easily it can be extended to meet custom requirements

Spring Security is a powerful and highly customizable authentication and access control framework. It is actually the standard for protecting spring based applications.

Spring Security is a framework that focuses on providing authentication and authorization for Java applications. Like all spring projects, the real strength of Spring Security is that it can be easily extended to meet customization requirements

You can know from the introduction of the official website that this is a permission framework. I think we didn't use the framework in our previous projects. How do we control permissions? Permissions are generally subdivided into function permissions, access permissions, and menu permissions. The code will be very cumbersome and redundant.

How to solve the cumbersome and redundant problem of writing permission code before, some mainstream frameworks came into being, and spring security is one of them.

Spring is a very popular and successful Java application development framework. Based on the spring framework, Spring Security provides a complete solution for Web application security. Generally speaking, the security of Web applications includes two parts: user Authentication and user Authorization. User Authentication refers to verifying whether a user is a legal subject in the system, that is, whether the user can access the system. User Authentication generally requires users to provide user name and password. The system completes the Authentication process by verifying the user name and password. User Authorization refers to verifying whether a user has permission to perform an operation. In a system, different users have different permissions. For example, for a file, some users can only read it, while others can modify it. Generally speaking, the system assigns different roles to different users, and each role corresponds to a series of permissions.

For the two application scenarios mentioned above, the Spring Security framework has good support. In terms of user authentication, the Spring Security framework supports mainstream authentication methods, including HTTP basic authentication, HTTP form authentication, http digest authentication, OpenID and LDAP. In terms of user authorization, Spring Security provides role-based access control and Access Control List (ACL), which can fine-grained control domain objects in applications.

Actual test

1, Implementation environment construction

1. Create an initial springboot project web module, thymeleaf module

2. Import static resources

welcome.html
|views
|level1
1.html
2.html
3.html
|level2
1.html
2.html
3.html
|level3
1.html
2.html
3.html
Login.html

3. controller jump!

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class RouterController {

    @RequestMapping({"/","/index"})
    public String index(){
        return "index";
    }

    @RequestMapping("/toLogin")
    public String toLogin(){
        return "views/login";
    }

    @RequestMapping("/level1/{id}")
    public String level1(@PathVariable("id") int id){
        return "views/level1/"+id;
    }

    @RequestMapping("/level2/{id}")
    public String level2(@PathVariable("id") int id){
        return "views/level2/"+id;
    }

    @RequestMapping("/level3/{id}")
    public String level3(@PathVariable("id") int id){
        return "views/level3/"+id;
    }

}

4. Test whether the experimental environment is OK!

2, Meet spring security

Spring Security is the security framework for the spring project and the default technology selection of the Spring Boot underlying security module. It can realize powerful Web security control. For security control, we only need to introduce the Spring Boot starter security module and configure a small amount to realize powerful security management!

Remember several classes:

  • WebSecurityConfigurerAdapter: custom Security policy
  • AuthenticationManagerBuilder: custom authentication policy
  • @Enable WebSecurity: enable WebSecurity mode

The two main goals of Spring Security are "authentication" and "authorization" (access control).

Authentication

Authentication is about verifying your credentials, such as user name / user ID and password, to verify your identity.

Authentication is usually done by user name and password, sometimes in combination with authentication factors.

Authorization

Authorization occurs after the system successfully verifies your identity, and will eventually grant you full access to resources (such as information, files, databases, funds, locations, almost anything).

This concept is universal, not just in Spring Security.

Authorization and certification

At present, everyone can access our test environment. We use Spring Security to add authentication and authorization functions

1. Introducing Spring Security module

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

2. Writing Spring Security configuration classes

Refer to the official website: https://spring.io/projects/spring-security

Check the version in our own project and find the corresponding help document:

https://docs.spring.io/spring-security/site/docs/5.3.0.RELEASE/reference/html5 #servlet-applications 8.16.4

3. Write basic configuration class

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 // Enable WebSecurity mode
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {

    }
}

4. Authorization rules for custom requests

@Override
protected void configure(HttpSecurity http) throws Exception {
    // Authorization rules for custom requests
    // The home page is accessible to everyone
    http.authorizeRequests().antMatchers("/").permitAll()
        .antMatchers("/level1/**").hasRole("vip1")
        .antMatchers("/level2/**").hasRole("vip2")
        .antMatchers("/level3/**").hasRole("vip3");
}

5. Test: I found that I can't get in except the home page! Because we don't have a login role at present, because the request requires that the login role has the corresponding permission!

6. Add the following configuration in the configure() method to enable the login function of automatic configuration!

// Turn on the auto configured login function
// /The login request comes to the login page
// /login?error redirection here indicates login failure
http.formLogin();

7. Test it: if you don't have permission, you will jump to the login page!

8. View the comments on the login page just now;

We can define authentication rules and override the configure(AuthenticationManagerBuilder auth) method

//Define authentication rules
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {

    //Defined in memory, you can also get it in jdbc
    auth.inMemoryAuthentication()
        .withUser("kuangshen").password("123456").roles("vip2","vip3")
        .and()
        .withUser("root").password("123456").roles("vip1","vip2","vip3")
        .and()
        .withUser("guest").password("123456").roles("vip1","vip2");
}

9. Test, we can use these accounts to log in and test! If you find it, you will report an error!

There is no PasswordEncoder mapped for the id "null"

10. The reason is that we need to encrypt the password transmitted from the front end in some way, otherwise we can't log in. We can register a coding configuration class

package com.fafa.config;

import org.springframework.context.annotation.Bean;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;

/**
 * @author Sire
 * @version 1.0
 * @date 2021-10-04 17:50
 */

@Component
public class MyPasswordEncoder implements PasswordEncoder {

    @Override
    public String encode(CharSequence charSequence) {
        return charSequence.toString();
    }

    @Override
    public boolean matches(CharSequence charSequence, String s) {
        return s.equals(charSequence.toString());
    }
}

11. After testing, it is found that the login is successful, and each role can only access the rules under its own authentication! Done

Permission control and logoff

1. Enable auto configured logoff

//Authorization rules for custom requests
@Override
protected void configure(HttpSecurity http) throws Exception {
    //....
    //Enable auto configured logoff
    // /Logout logout request
    http.logout();
}

2. In the front end, we add a logout button in the index.html navigation bar

<a class="item" th:href="@{/logout}">
    <i class="address card icon"></i> cancellation
</a>

3. We can test it. After the login is successful, click logout. If we find that the logout is completed, we will jump to the login page!

4. However, we want him to log off successfully and still jump to the home page. What should we do?

// .logoutSuccessUrl("/");  Log out successfully and come to the home page
http.logout().logoutSuccessUrl("/");

5. Test. After logging out, find that you can jump to the home page. OK

6. We now have another requirement: when the user does not log in, only the login button is displayed on the navigation bar. After the user logs in, the navigation bar can display the login user information and logout button! Also, for example, kuangshen has only vip2 and vip3 functions, so only these two functions are displayed when logging in, but the function menu of vip1 is not displayed! This is the real website! What should I do?

We need to combine some functions in thymeleaf

sec: authorize = "isAuthenticated()": whether to authenticate login! To display different pages

Maven dependency:

<!-- https://mvnrepository.com/artifact/org.thymeleaf.extras/thymeleaf-extras-springsecurity4 -->
<dependency>
    <groupId>org.thymeleaf.extras</groupId>
    <artifactId>thymeleaf-extras-springsecurity5</artifactId>
    <version>3.0.4.RELEASE</version>
</dependency>

7. Modify our front page

  1. Import namespace

  2. xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5"
    
  3. Modify the navigation bar and add authentication judgment

  4. <!--Login and logout-->
    <div class="right menu">
    
        <!--If you are not logged in-->
        <div sec:authorize="!isAuthenticated()">
            <a class="item" th:href="@{/login}">
                <i class="address card icon"></i> Sign in
            </a>
        </div>
    
        <!--If logged in-->
        <div sec:authorize="isAuthenticated()">
            <a class="item">
                <i class="address card icon"></i>
                user name:<span sec:authentication="principal.username"></span>
                Role:<span sec:authentication="principal.authorities"></span>
            </a>
        </div>
    
        <div sec:authorize="isAuthenticated()">
            <a class="item" th:href="@{/logout}">
                <i class="address card icon"></i> cancellation
            </a>
        </div>
    </div>
    
  5. 8. Restart the test. We can log in and try. After successful login, the page we want is displayed;

  6. 9. If you log off 404, it is because it prevents csrf Cross Site Request Forgery by default, because it will cause security problems. We can change the request to post form submission, or turn off the csrf function in spring security; Let's try this: add http.csrf().disable();

  7. http.csrf().disable();//Turn off csrf function: Cross Site Request Forgery. By default, logout requests can only be submitted through post
    http.logout().logoutSuccessUrl("/");
    
  8. 10. We continue to complete the following role function block authentication!

  9. <!-- sec:authorize="hasRole('vip1')" -->
    <div class="column" sec:authorize="hasRole('vip1')">
        <div class="ui raised segment">
            <div class="ui">
                <div class="content">
                    <h5 class="content">Level 1</h5>
                    <hr>
                    <div><a th:href="@{/level1/1}"><i class="bullhorn icon"></i> Level-1-1</a></div>
                    <div><a th:href="@{/level1/2}"><i class="bullhorn icon"></i> Level-1-2</a></div>
                    <div><a th:href="@{/level1/3}"><i class="bullhorn icon"></i> Level-1-3</a></div>
                </div>
            </div>
        </div>
    </div>
    
    <div class="column" sec:authorize="hasRole('vip2')">
        <div class="ui raised segment">
            <div class="ui">
                <div class="content">
                    <h5 class="content">Level 2</h5>
                    <hr>
                    <div><a th:href="@{/level2/1}"><i class="bullhorn icon"></i> Level-2-1</a></div>
                    <div><a th:href="@{/level2/2}"><i class="bullhorn icon"></i> Level-2-2</a></div>
                    <div><a th:href="@{/level2/3}"><i class="bullhorn icon"></i> Level-2-3</a></div>
                </div>
            </div>
        </div>
    </div>
    
    <div class="column" sec:authorize="hasRole('vip3')">
        <div class="ui raised segment">
            <div class="ui">
                <div class="content">
                    <h5 class="content">Level 3</h5>
                    <hr>
                    <div><a th:href="@{/level3/1}"><i class="bullhorn icon"></i> Level-3-1</a></div>
                    <div><a th:href="@{/level3/2}"><i class="bullhorn icon"></i> Level-3-2</a></div>
                    <div><a th:href="@{/level3/3}"><i class="bullhorn icon"></i> Level-3-3</a></div>
                </div>
            </div>
        </div>
    </div>
    
  10. 11. Test it!

  11. 12. Permission control and logout!

Remember me

In the current situation, as long as we log in, close the browser and log in again, we will log in again. However, in the case of many websites, there is a function of remembering passwords. How can we achieve this? It's simple

1. Enable remember me function

//Authorization rules for custom requests
@Override
protected void configure(HttpSecurity http) throws Exception {
    //. . . . . . . . . . . 
    //Remember me
    http.rememberMe();
}

2. We started the project again and found that the login page had an additional remember me function. After we logged in, we closed the browser, and then reopened the browser to access. We found that the user still exists!

Thinking: how to achieve it? It's actually very simple

We can view the browser's cookie s

3. When we click log off, we can find that spring security automatically deletes this cookie for us

4. Conclusion: after successful login, send the cookie to the browser for saving. After login, take this cookie with you. As long as you pass the check, you can avoid login. If you click log off, the cookie will be deleted. We have talked about the specific principle in the Java Web stage, so we won't talk more here!

Custom landing page

Now this Login page is the default of spring security. How can we use the Login interface written by ourselves?

1. Specify loginpage after the login page configuration just now

http.formLogin().loginPage("/toLogin");

2. Then the front end also needs to point to the login request defined by ourselves

<a class="item" th:href="@{/toLogin}">
    <i class="address card icon"></i> Sign in
</a>

3. When we log in, we also need to configure where to send these information. login.html configures the submission request and method. The method must be post:

The notes in the source code of loginPage() state:

<form th:action="@{/login}" method="post">
    <div class="field">
        <label>Username</label>
        <div class="ui left icon input">
            <input type="text" placeholder="Username" name="username">
            <i class="user icon"></i>
        </div>
    </div>
    <div class="field">
        <label>Password</label>
        <div class="ui left icon input">
            <input type="password" name="password">
            <i class="lock icon"></i>
        </div>
    </div>
    <input type="submit" class="ui blue submit button"/>
</form>

4. When this request is submitted, we still need to verify it. What should we do? We can check the source code of formLogin() method! We configure the parameters to receive the login user name and password!

http.formLogin()
    .usernameParameter("username")
    .passwordParameter("password")
    .loginPage("/toLogin")
    .loginProcessingUrl("/login"); // Login form submission request

5. Add the remember me multi selection box on the login page

<input type="checkbox" name="remember"> Remember me

6. Back end validation processing!

//Customize and remember my parameters!
http.rememberMe().rememberMeParameter("remember");

7. Test, OK

Full configuration code

package com.fafa.config;

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.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

/**
 * @author Sire
 * @version 1.0
 * @date 2021-10-04 13:22
 *
 * @EnableWebSecurity Enable WebSecurity mode
 */

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    /**
     * Authorization rules for custom requests
     * The home page is accessible to everyone
     * **/
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().antMatchers("/").permitAll()
            .antMatchers("/level1/**").hasRole("vip1")
            .antMatchers("/level2/**").hasRole("vip2")
            .antMatchers("/level3/**").hasRole("vip3");
        // Turn on the auto configured login function
        // login Request to go to the login page
        // login?error redirect
        //Login form submission request
        http.formLogin()
            .usernameParameter("username")
            .passwordParameter("password")
            .loginPage("/toLogin")
            .loginProcessingUrl("/login");

        // Turn on the auto configured logoff function
        http.logout();

        //Turn off the csrf function and cross site request forgery. By default, logout requests can only be submitted through post
        http.csrf().disable();
        // .logoutSuccessUrl("/"); Log out successfully and come to the home page
        http.logout().logoutSuccessUrl("/");

        //Remember my custom parameters!
        http.rememberMe().rememberMeParameter("remember");
    }

    /** Define authentication rules**/
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {

        //Defined in memory, you can also get it in jdbc
        //Spring security 5.0 adds a variety of encryption methods and changes the password format.
        //If we want our project to log in normally, we need to modify the code in configure. We need to encrypt the password transmitted from the front end in some way
        //bcrypt encryption is officially recommended by spring security.
        auth.inMemoryAuthentication()
            .withUser("admin").password("123456").roles("vip1","vip2","vip3")
            .and()
            .withUser("root").password("root").roles("vip1")
            .and()
            .withUser("guest").password("000").roles("vip2");
    }
}

Posted by Firestorm3d on Wed, 13 Oct 2021 13:31:07 -0700