The most simple and comprehensive tutorial of Spring Security (with Demo)

1. Introduction to Spring Security

Spring Security is a security framework that provides declarative security protection for Spring-based applications. It provides a complete security solution that handles identity verification and authorization at the web request level and method invocation level. Because it is based on the Spring framework, Spring Security makes full use of dependency injection and aspect-oriented technologies.

Spring Security mainly solves security problems from two aspects:

  1. Web request level : Use the filters in the Servlet specification to protect web requests and restrict access at the URL level.

  2. Method invocation level : Use Spring AOP to protect method invocations, ensuring that only users with the appropriate permissions can access the secured methods.

If you want to have an in-depth understanding of the related concepts and implementation principles of Spring Security, you can click on the portal == " Basic Principles of Spring Security

Second, Spring Security's Web request-level security Demo

1. Create a new Spring project and add springsecurity dependencies to the pom.

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

2. Create a configuration class for Spring Security

package com.spring.security.springsecurity.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;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

@Configuration
@EnableWebSecurity  //启用Web安全功能
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                //访问"/"和"/home"路径的请求都允许
                .antMatchers("/", "/home","/staff","/staff/*")
                .permitAll()
                //而其他的请求都需要认证
                .anyRequest()
                .authenticated()
                .and()
                //修改Spring Security默认的登陆界面
                .formLogin()
                .loginPage("/login")
                .permitAll()
                .and()
                .logout()
                .permitAll();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception{
        //基于内存来存储用户信息
        auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
                .withUser("user").password(new BCryptPasswordEncoder().encode("123")).roles("USER").and()
                .withUser("admin").password(new BCryptPasswordEncoder().encode("456")).roles("USER","ADMIN");
    }
}

Code analysis:

@EnableWebSecurity annotation: Enable web security function (but it is useless by itself, Spring Security configuration class also needs to implement WebSecurityConfigurer or inherit WebSecurityConfigurerAdapter class, the latter is used in this Demo, because it is easier to configure and use).

@EnableWebMvcSecurity annotation: Deprecated in Spring 4.0.

WebSecurityConfigurerAdapter class: You can specify the details of Web security by overloading the three configure() methods of this class.

1. configure(WebSecurity): By overloading this method, the Filter chain of Spring Security can be configured.

2. configure(HttpSecurity): By overriding this method, you can configure how to protect requests through interceptors.

How to configure the protection path

method

can do

access(String)

Allows access if the given SpEL expression evaluates to true

anonymous()

Allow anonymous user access

authenticated()

Allow authenticated users to access

denyAll ()

Unconditionally deny all access

fullyAuthenticated()

If the user is fully authenticated (not authenticated by the Remember-me function), access is allowed

hasAnyAuthority(String…)

Allow access if the user has one of the given permissions

hasAnyRole(String…)

Allow access if the user has one of the given roles

hasAuthority(String)

Allow access if the user has the given permission

hasIpAddress(String)

Allow access if the request comes from the given IP address

hasRole(String)

Allow access if the user has the given role

not()

Negate the results of other access methods

allowAll()

unconditional access

rememberMe()

Allow access if the user is authenticated by the Remember-me feature

All SpEL expressions supported by Spring Security are as follows:

safe expression

Calculation results

authentication

User authentication object

denyAll

the result is always false

hasAnyRole(list of roles)

true if the user is granted any of the specified permissions

hasRole(role)

true if the user has been granted the specified permission

hasIpAddress(IP Adress)

User address

isAnonymous()

Is it an anonymous user

isAuthenticated()

not an anonymous user

isFullyAuthenticated

Not anonymous nor remember-me authentication

isRemberMe()

remember-me certification

allowAll

always true

principal

User main information object

3. configure(AuthenticationManagerBuilder): By overloading this method, the user-detail (user details) service can be configured.

How to configure user details

method

describe

accountExpired(boolean)

Defines whether the account has expired

accountLocked(boolean)

Defines if the account is locked

and()

for connection configuration

authorities(GrantedAuthority…)

Grant one or more permissions to a user

authorities(List)

Grant one or more permissions to a user

authorities(String…)

Grant one or more permissions to a user

credentialsExpired(boolean)

Defines if the credential has expired

disabled(boolean)

Defines whether the account has been disabled

password(String)

Define the user's password

roles(String…)

Grant a user one or more roles

There are three ways to store user information:

**1. Use memory-based user storage: **Through inMemoryAuthentication()methods, we can enable, configure and arbitrarily populate memory-based user storage. And, we can call withUser()the method to add a new user to the in-memory user store, the parameter of this method is username. withUser()The method returns UserDetailsManagerConfigurer.UserDetailsBuilder, which provides several methods for further configuring the user, including methods for setting the user's password password()and granting one or more role permissions for a given user roles(). Note that roles()method is authorities()a shorthand for method. roles()The values ​​given to the method are prefixed with a ROLE_prefix and granted to the user as a permission. Therefore, the authority of the appeal code user is: ROLE_USER,ROLE_ADMIN。而with the help of a passwordEncoder()method to specify a password encoder (encoder), we can encrypt and store the user password.

**2. Authentication based on database tables: **User data is usually stored in a relational database and accessed through JDBC. To configure Spring Security to use a JDBC-backed user store, we can use the jdbcAuthentication()method and configure its DataSource so that we can access the relational database.

**3. LDAP-based authentication: **In order for Spring Security to use LDAP-based authentication, we can use the ldapAuthentication()method.

3. Controller layer code and front-end code

The SecurityController code is as follows:

package com.spring.security.springsecurity.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class SecurityController {

    @GetMapping(value = {"/home","/"})
    public String home(){
        return "home";
    }

    @GetMapping(value = "/hello")
    public String hello(){
        return "hello";
    }

    @GetMapping(value = "/login")
    public String login(){
        return "login";
    }
}

home.html

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
    <head>
        <title>Spring Security Example</title>
    </head>
    <body>
        <h1>Welcome!</h1>

        <p>Click <a th:href="@{/hello}">here</a> to see a greeting.</p>
    </body>
</html>

hello.html

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
      xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
    <head>
        <title>Hello World!</title>
    </head>
    <body>
        <h1 th:inline="text">Hello [[${#httpServletRequest.remoteUser}]]!</h1>
        <form th:action="@{/logout}" method="post">
            <input type="submit" value="Sign Out"/>
        </form>
    </body>
</html>

login.html

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
      xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
    <head>
        <title>Spring Security Example </title>
    </head>
    <body>
        <div th:if="${param.error}">
            Invalid username and password.
        </div>
        <div th:if="${param.logout}">
            You have been logged out.
        </div>
        <form th:action="@{/login}" method="post">
            <div><label> User Name : <input type="text" name="username"/> </label></div>
            <div><label> Password: <input type="password" name="password"/> </label></div>
            <div><input type="submit" value="Sign In"/></div>
        </form>
    </body>
</html>

4. Operation results and analysis

When we visit localhost:8080/hello directly, the page will jump to http://localhost:8080/login , this is because the SecurityConfig class configures only requests for "/" and "/home" paths without logging in can be accessed, while other requests require authentication. (Note: I have changed the port here to 8081 because I have made adjustments. You can access port 8080 as usual.)

At this point, we enter in the login interface, we configure the user name and password based on memory storage in SecurityConfig, and we will successfully jump to the hello interface

Three, Spring Security's method invocation level security Demo

Spring Security provides three different security annotations:

  • Spring Security's own @Secured annotation;
  • @RolesAllowed annotation of JSR-250;
  • Expression-driven annotations, including @PreAuthorize, @PostAuthorize, @PreFilter, and @PostFilter.

annotation

describe

@PreAuthorize

Restrict access to methods based on the evaluation of expressions before the method invocation

@PostAuthorize

Method invocation is allowed, but a security exception will be thrown if the expression evaluates to false

@PostFilter

Method invocation is allowed, but the result of the method must be filtered by expression

@PreFilter

Method calls are allowed, but input values ​​must be filtered before entering the method

1. Enable annotation-based method security

In Spring, if you want to enable annotation-based method security, the key is to use @EnableGlobalMethodSecurity on the configuration class, such as:

package com.spring.security.springsecurity.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration;

@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true,jsr250Enabled = true,prePostEnabled = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {

}

Here we set securedEnabled = true, then Spring will create a pointcut and prevent methods annotated with @Secured from entering the aspect. Similarly, jsr250Enabled = true and prePostEnabled = true, respectively enable @RolesAllowed and expression-driven annotations.

After configuring the MethodSecurityConfig class at this point, we can add a method to the SecurityController based on the appeal code:

    @GetMapping(value = "/admin")
    @Secured("ROLE_ADMIN")
    public String admin(){
        return "admin";
    }

The admin.html interface is as follows:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>admin</title>
</head>
<body>
    <h1>admin page</h1>
</body>
</html>

At this point, how do we access the http://localhost:8081/admin path as the user user, and a 403 error will occur.

If you access the http://localhost:8081/admin path as the admin user, the following interface will be displayed, indicating successful access.

Guess you like

Origin blog.csdn.net/m0_54850467/article/details/123683180