Ten minutes to get you started with Spring Security

🏡 Blog Homepage: Pai Daxing

⛳️ Welcome to follow ❤️ Like 🎒 Favorites ✏️ Comment

🎢 This article was originally edited by Pai Daxing

🚧 Series column: “Security [Framework] “

🎈 Technology should be applied, not just at the learning stage

Table of contents

🍉 Preschool test

Add configuration class

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.formLogin() //Form login
            .and()
            .authorizeRequests() //authentication configuration.anyRequest 
        () //any 
        request.authenticated(); //authentication is required
    }
}

The SpringSecurity startup project has a default login page

🍒 Related concepts in permission management

Subject :principal

Users who use the system or devices that log in remotely from other systems, etc. Simply put, whoever uses the system is the subject

Certification:authentication

Authorization:authorization

① Add a controller for access

@RestController
public class IndexController {

    @GetMapping("/index")
    public String index(){
        return "success";
    }
}

Test Results

② Basic Principles of Spring Security

Spring Security is essentially a filter chain, and the filter chain can be obtained from startup

org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter
org.springframework.security.web.context.SecurityContextPersistenceFilter 
org.springframework.security.web.header.HeaderWriterFilter
org.springframework.security.web.csrf.CsrfFilter
org.springframework.security.web.authentication.logout.LogoutFilter 
org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter 
org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter 
org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter
org.springframework.security.web.savedrequest.RequestCacheAwareFilter
org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter
org.springframework.security.web.authentication.AnonymousAuthenticationFilter 
org.springframework.security.web.session.SessionManagementFilter 
org.springframework.security.web.access.ExceptionTranslationFilter 
org.springframework.security.web.access.intercept.FilterSecurityInterceptor

The underlying process of the code: focus on three filters:

FilterSecurityInterceptor: is a method-level permission filter, basically at the bottom of the filter chain

super.beforeInvocation(filterInvocation)Indicates to check filterwhether the previous pass

filterInvocation.getChain().doFilter(filterInvocation.getRequest(),filterInvocation.getResponse)Indicates the real call background service

ExceptionTranslationFilter: is an exception filter used to handle exceptions thrown during authentication and authorization

UsernamePasswordAuthenticationFilter: Intercept /loginthe POSTrequest and verify the username and password in the form

③ How are filters loaded?

  • Using the SpringSecurityconfig filter chain
    • DelegatingFilterProxy

by getBean()getting the name of the filterFilterChainProxy

Add an existing filter to the filter chain by this.getFilters()addingList<Filter>

④ UserDetailsService interface

When nothing is configured, the account and password are SpringSecuritygenerated by the definition, and in the actual project, the account and password are queried from the database. So you need to control the authentication logic through custom logic.

return value:UserDetails

public interface UserDetails extends Serializable {

    /**
     * Returns the permissions granted to the user, cannot return null
     */
    Collection<? extends GrantedAuthority> getAuthorities();

    /**
     * Returns the password used to authenticate the user
     */
    String getPassword();

    /**
     * Returns the username used to authenticate the user, not null
     */
    String getUsername();

    /**
     * Indicates whether the account has expired
     */
    boolean isAccountNonExpired();

    /**
     * Indicates whether the account is locked
     */
    boolean isAccountNonLocked();

    /**
     * Indicates whether the credential {password} has expired
     */
    boolean isCredentialsNonExpired();

    /**
     * Indicates whether the current user is available
     */
    boolean isEnabled();

}

The following is the implementation class of UserDetails

We only need to use Userthis entity class in the future!

  • Method parameters:username

Indicates the username. This value is the data passed by the client form. It must be called by default username, otherwise it cannot be received!

⑤ PasswordEncoder interface

//Indicates that the parameters are parsed according to specific parsing rules 
String encode (CharSequence rawPassword) ;
 /**
    Indicates to verify whether the encoded password obtained from the storage matches the original password submitted after encoding. If the password matches, it returns true, if it does not match, it returns false. The first parameter indicates the password to be parsed, and the second parameter Indicates the stored password
*/ 
boolean  matches (CharSequence rawPassword,String encodedPassword) ;
 //Indicates that if the parsed password can be parsed again, and a more secure result is reached, return true, otherwise return false, and return false by 
default default  boolean  upgradeEncoding (String encodedPassword) {
     return  false ;
}

BCryptPasswordEncoderIt is the Spring Securityofficially recommended password parser, and this parser is usually used.

BCryptPasswordEncoderIt is bcrypta concrete implementation of the strong hashing method. It is a one-way encryption based on an Hashalgorithm, which can be controlled by strengthcontrolling the encryption strength. The default is10

test

@Test
public void testBCrypt() {
   // Create password parser 
  BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
   // Encrypt 
  password String password = passwordEncoder.encode( "admin" );System.out 
  .println ( "Encrypted password: " +password);

  //Determine whether the original password is the same as the encrypted password 
  boolean isTrue = passwordEncoder.matches( "admin" , password);System.out.println ( "Is the password equal:" +isTrue) 
  ;
}

2.6 SpringBoot’s automatic configuration of Security

https://docs.spring.io/springsecurity/site/docs/5.3.4.RELEASE/reference/html5/#servlet-hello

🌽 SpringSecurity Web Permission Scheme

① Set the login user name and password

  • Method 1: Configuration file

spring.security.user.name=admin
spring.security.user.password=admin

  • Method 2: Configuration class

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }


    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        String password = passwordEncoder.encode("123");
        auth.inMemoryAuthentication().withUser("lucy").password(password).roles("admin");
    }
}

  • Method 3: Customize the implementation class

The first step is to create a configuration class and set which userDetailsService implementation class to use

Step 2: Write an implementation class and return the User object. The User object has username, password and operation permissions

② The custom settings login page can be accessed without authentication

  • Implement the relevant configuration in the configuration class

@Override
    protected void configure(HttpSecurity http) throws Exception {
        http.formLogin() //Customize your own login 
            page.loginPage( "/login.html" ) //Login page settings 
            //After filling in the username and password, you need to submit the form operation to the specified Controller.loginProcessingUrl 
            ( " /user/login" ) //Login access path.defaultSuccessUrl 
            ( "/index" ).permitAll() //The default login is successful, the path to jump to 
            . and ().authorizeRequests() //Define which urls require authentication, Which do not require authentication.antMatchers 
            ( "/" , "/index" , "/user/login" ).permitAll() //Set which paths can be accessed directly without authentication.anyRequest 
            ().authenticated() //All requests require authentication 
            .and ().csrf().disable(); //Close csrf protection 
    }

③ Access control based on roles or permissions

(1) hasAuthority method

Returns if the current principal has the specified permission true, otherwise returnsfalse

  • Modify configuration class

  • Modify MyUserDetailsServicemanually add permissions to the logged in user

    • Permission isadmins
(2) hasAnyAuthority method

Returns if the current principal has any of the provided roles (given as a comma-separated list of strings)true

(3) hasRole method

Access is allowed if the user has the given role, otherwise appears403

Returns if the current principal has the specified roletrue

underlying source code

private static String hasRole(String role) {
        Assert.notNull(role, "role cannot be null");
        Assert.isTrue(!role.startsWith("ROLE_"),
                () -> "role should not start with 'ROLE_' since it is automatically inserted. Got '" + role + "'");
        return "hasRole('ROLE_" + role + "')";
    }

Note: Since the bottom layer has screened the role information to determine whether the role ROLE_starts withROLE_

(4) hasAnyRole

Indicates that the user can access with any condition.

Add roles to users:

Modify configuration class

④ Customize 403 No permission to access the page

Configure in the configuration class

⑤ Use of annotations

(1) @Secured

Determine whether it has a role, and note that the matched string here needs to be prefixed ROLE_.

You need to turn on the annotation function before using it!

@EnableGlobalMethodSecurity(securedEnabled = true)

@EnableGlobalMethodSecurity(securedEnabled = true)
@SpringBootApplication
@MapperScan("com.pdx.mapper")
public class SpringSecurity03Application {

    public static void main(String[] args) {
        SpringApplication.run(SpringSecurity03Application.class, args);
    }

}

Write front controller using annotations, set roles

@GetMapping("/update")
@Secured({"ROLE_sale","ROLE_manager"})
@ResponseBody
public String update(){
  return "hello update";
}

userDetailsService sets user roles

(2) @PreAuthorize

First enable the annotation function:

@EnableGlobalMethodSecurity(prePostEnabled = true)

@EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled = true)

@PreAuthorize: The annotation is suitable for permission verification before entering the method, and @PreAuthorizethe parameters of the logged-in user can be roles/permissionspassed to the method.

Add annotations to controller methods

(3) @PostAuthorize

Turn on the annotation function first

@EnableGlobalMethodSecurity(prePostEnabled = true)

@PostAuthorizeThe annotation is not used much, and the permission verification is performed after the method is executed, which is suitable for verifying the permission with the return value

Add annotations to controller methods

If a user who does not have permission to access this resource is used to access this resource, it will definitely jump to the 403 page we customized before, but in fact this method has already been executed!

(4) @PostFilter【Use less】

@PostFilter: Filter the data after authorization verification, leaving the data with the user name admin1

The reference in the expression refers to an element in the filterObjectreturn value of the methodList

@GetMapping("/getAll")
@PostAuthorize("hasAnyAuthority('admins')")
@PostFilter("filterObject.username == 'admin1'")
public List<Users> getAllUsers(){
  List<Users> list = new ArrayList<>();
  list.add(new Users(11,"admin1","666"));
  list.add(new Users(21,"admin2","888"));
  System.out.println(list );
  return list;
}

(5) @PreFilter【Use less】

@PreFilter: filter the data before entering the controller

@GetMapping("/putAll")
@PreAuthorize("hasAnyAuthority('admins')")
@PreFilter(value = "filterObject.id%2==0")
public List<Users> putAll(@RequestBody List<Users> list){
  list.forEach(t ->{
    System.out.println(t.getId()+"\t"+t.getUsername());
  });
  return list;
}

⑥ User logout

(1) Add a logout link on the login page

< body > 
  Login successful < br > 
  < a  href = "/logout" > Logout </ a > 
</ body >

Modify configuration class

⑦ Automatic login (Remember me)

(1) Implementation principle

Step 1: Create the related table

create table {{EJS0}}(
   {{EJS1}} varchar(64) not null,
   {{EJS2}} varchar(64) not null,
   {{EJS3}} varchar(64) not null,
   {{EJS4}} TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
   PRIMARY KEY({{EJS5}})
);

Step 2: Configure classes, inject data sources, configure and operate database objects

//Inject data source 
@Autowired 
private DataSource dataSource;

//Configuration object 
@Bean 
public PersistentTokenRepository persistentTokenRepository () {
  JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
  jdbcTokenRepository.setDataSource(dataSource);
  //Whether to create a table at startup 
  //jdbcTokenRepository.setCreateTableOnStartup(true); 
  return jdbcTokenRepository;
}

Step 3: Configure automatic login in the configuration class

Step 4: After login.htmladdingcheckbox

⑧ CSRF

(1) CSRF understanding
(2) Example

Add a hidden field to the login page

<input type="hidden" th:if="${_csrf}!=null" th:value="${_csrf.token}" />

Turn off the security configuration class incsrf

That’s it for the introduction of Spring Security. I believe you have learned a lot.
❗ ❗ The ultimate goal of learning is to not only learn but also to apply technology.❗ ❗

Leave a Comment

Your email address will not be published. Required fields are marked *