1. Introduction to Spring Security

Spring Security is a powerful and highly customizable authentication and access control framework. It is the standard for ensuring Spring-based applications — from the official reference manual

Like Shiro, Spring Security has authentication, authorization, encryption, and other capabilities for permission management. Unlike Shiro, Spring Security has more functionality than Shiro, and Spring Security is a better fit for Springboot than Shiro because they are members of the Spring family. Today, we’ll integrate Spring Security for the SpringBoot project.

The version used in this article:

SpringBoot: 2.2.6.RELEASE Spring Security: 5.2.2

2. Configure Spring Security

Integrating Spring Security with SpringBoot is as simple as adding the following code to pom.xml:

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

Instead of specifying the version number of Spring Security, it will match the corresponding SpringBoot version, which is 2.2.6.RELEASE and Spring Security 5.2.2.RELEASE.

Then we can start SpringBoot.

When we try to access the project, it jumps to this screen:

Right! Until then, you don’t have to do anything. This is the beauty of Spring Security. You just need to import the Spring Security package and it will work in your project. Because it already helps you to implement a simple login interface. According to the official introduction, the login account is user, and the password is a random password, which can be found in the console, like this sentence:

    Using generated security password: 1cb77bc5-8d74-4846-9b6c-4813389ce096Copy the code

Using generated security password after the random password, we can use this password to log in. Random passwords are generated every time you start the service (keep an eye on the console if you’ve configured DevTools for hot deployment, because every time you change the code, the system will reboot and the random passwords will be generated again).

Of course, that’s not what you want, and it’s certainly not what you give your users. So, next, let’s configure it the way we want.

To implement a custom configuration, the first to create an inheritance in WebSecurityConfigurerAdapter configuration class:

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

}
Copy the code

This uses the @enablewebsecurity annotation, which Spring Security uses to EnableWebSecurity. I won’t go into the implementation here.

To implement a custom interception configuration, Spring Security must first be told where to get the user’s information and what role the user corresponds to. There needs to be rewritten WebSecurityConfigurerAdapter configure (AuthenticationManagerBuilder auth) method. This method instructs Spring Security to find the list of users, compare it to the user who wants to pass the interceptor, and perform the following steps.

Spring Security has several options for user storage configuration, including:

  • Memory user storage
  • Database user storage
  • LDAP User Storage
  • Custom user storage

Let’s take a look at each of these user storage configurations:

1. Memory User storage

This configuration directly stores user information in memory, which is by far the fastest. But it only works for a limited number of users, and those users rarely change. Let’s look at the configuration method:

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication().passwordEncoder(passwordEncoder())
            .withUser("zhangsan").password(passwordEncoder().encode("123456")).authorities("ADMIN")
            .and()
            .withUser("lisi").password(passwordEncoder().encode("123456")).authorities("ORDINARY");
    }

    private PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }Copy the code

As you can see, AuthenticationManagerBuilder use initializer way to build. In the above method, the inMemoryAuthentication() method is first called, which specifies that the user is stored in memory. You then call the passwordEncoder() method, which tells Spring Security how to encrypt the authentication password. Because after Spring Security5, you must specify some kind of encryption or the program will report an error. The withUser(), password(), and authorities() methods are then invoked to specify the user’s account, password, and authority name, respectively. After adding a user, the and() method is used to connect the addition of the next user.

If you use this configuration approach, you’ll find that you have to change the code when you change the user. For most projects, this approach is not sufficient, at least we need a registration function.

2. Database user storage

The user information is stored in the database, so that we can easily add, delete, change and check the user information. It can also add additional information to the user in addition to authentication information, which is the way many of our careful applications are designed. Let’s implement the following:

@Autowired private DataSource dataSource; @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.jdbcAuthentication().dataSource(dataSource).passwordEncoder(passwordEncoder()) .usersByUsernameQuery( "select username, password, status from Users where username = ?" ) .authoritiesByUsernameQuery( "select username, authority from Authority where username = ?" ); } private PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); }Copy the code

Call jdbcAuthentication() to tell Spring Security to use JDBC to query users and permissions, the dataSource() method specifies database connection information, and the passwordEncoder() method specifies password encryption rules, The user’s password data should be encrypted and stored in the same way. Otherwise, two passwords with different encryption methods will be matched and replaced. UsersByUsernameQuery () and authoritiesByUsernameQuery () method, respectively, defines the SQL query information users and permissions. In fact, Spring Security provides us with default SQL for querying users, permissions, and even group user authorization, The three default SQL stored in org. Springframework. Security. Core. The populated userdetails. JDBC. JdbcDaoImpl, interested friends can go in and have a look. If you want to use the default, key fields in your table must match those in the statement.

Using a database to store user and permission information already meets most of the requirements. But Spring Security provides another way to configure, and let’s take a look.

3.LDAP user storage

LDAP: Lightweight directory access protocol. It is an open, neutral, and industry-standard application protocol that provides access control and maintains directory information of distributed information over IP. In simple terms, it is the technology of storing user information on another server (of course, it can also be on the same server, but we generally do not do this) and accessing it over the network.

Let’s configure it briefly:

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        LdapAuthenticationProviderConfigurer<AuthenticationManagerBuilder> configurer =                     auth.ldapAuthentication()
            .userSearchBase("ou=people")
            .userSearchFilter("(uid={0})")
            .groupSearchBase("ou=groups")
            .groupSearchFilter("member={0}");

        configurer.passwordCompare()
            .passwordEncoder(passwordEncoder())
            .passwordAttribute("passcode");
        configurer.contextSource().url("ldap://xxxxx.com:33389/dc=xxxxxx,dc=com");
    }

    private PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }Copy the code

UserSearchFilter () and groupSearchFilter() set the filter criteria for users and groups, while userSearchBase() and groupSearchBase() set the search start location. ContextSource ().url() Sets the IP address of the LDAP server. If no remote server is available, the embedded LDAP server can be used using contextSource().root(), which uses user data files in the project to provide authentication services.

If the above methods do not meet our requirements, we can use a custom way to configure.

4. Customize user storage

Custom user storage, which uses the authentication name to find the corresponding user data and hand it over to Spring Security for use. We need to define a service class that implements UserDetailsService:

@Service
public class MyUserDetailsService implements UserDetailsService{

    @Autowired
    private UserMapper userMapper;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userMapper.getUserByUsername(username);
        return user == null ? new User() : user;
    }
}

public class User implements UserDetails {
    ...
}Copy the code

The class only needs to implement one method: loadUserByUsername(). What this method does is use the passed username to match a user entity with information such as a password. Note that the User class needs to implement UserDetails, which means that the information retrieved must have the information required by Spring Security.

Let’s continue with the configuration:

@Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private MyUserDetailsService userDetailsService; @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService) .passwordEncoder(passwordEncoder()); } @Bean private PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); }}Copy the code

This configuration is simple, just tell Spring Security what your UserDetailsService implementation class is, and it calls loadUserByUsername() to find the user.

These are the four ways Spring Security provides for storing users, and the next thing you need to think about is how to intercept requests.

Request interception

1. Safety rules

Spring Security’s request interception configuration method is an overloading of the user storage configuration method. Let’s start with a brief configuration:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
            .antMatchers("/user", "/menu")
            .hasRole("ADMIN")
            .antMatchers("/", "/**").permitAll();
    }
}Copy the code

After calling the authorizeRequests() method, you can add a custom interception path. The antMatchers() method configures the request path, hasRole() and permitAll() specify access rules, indicating that only users with ADMIN permission can access and all users can access, respectively.

Note that the configurations need to come in pairs, and the order in which they are configured is important. Rules declared earlier have higher precedence. That is, if we put.antmatchers (“/”, “/”).permitall ()** first, like this:

@Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
            .antMatchers("/", "/**").permitAll()
             .antMatchers("/user", "/menu")
            .hasRole("ADMIN");
    }Copy the code

Then, the following configuration of “/user” and “/menu” is futile, because the previous rule already indicates that all paths can be accessed by everyone. Of course, there are many more rules for permissions, but I’ve just listed two here. The following are common built-in expressions:

express describe
hasRole(String role) returntrueWhether the current client has a specified role. For example,hasRole('admin')By default, provided roles that do not start with “ROLE_” are added. You can modify itdefaultRolePrefixOn customizationDefaultWebSecurityExpressionHandler.
HasAnyRole (String... roles) returntrueWhether the current principal has any of the roles provided (in the form of a comma-separated list of strings). For example,hasAnyRole('admin', 'user')By default, provided roles that do not start with “ROLE_” are added. You can modify itdefaultRolePrefixOn customizationDefaultWebSecurityExpressionHandler.
hasAuthority(String authority) returntrueWhether the current client has designated authority. For example,hasAuthority('read')
HasAnyAuthority (String... authorities) returntrueIf the current body has any of the supplied authorities (given a comma-separated list of strings) for example,hasAnyAuthority('read', 'write')
principal Allows direct access to the principal object representing the current user
authentication Allow direct accessAuthenticationfromSecurityContext
permitAll Always evaluated astrue
denyAll Always evaluated asfalse
isAnonymous() returntrueWhether the current client is an anonymous user
isRememberMe() returntrueWhether the current subject is a Remember Me user
isAuthenticated() trueReturns if the user is not anonymous
isFullyAuthenticated() returntrueIf the user is not anonymous or remember my user
hasPermission(Object target, Object permission) returntrueWhether a user can access a given target for a given permission. For example,hasPermission(domainObject, 'read')
hasPermission(Object targetId, String targetType, Object permission) returntrueWhether a user can access a given target for a given permission. For example,hasPermission(1, 'com.example.domain.Message', 'read')

In addition, there is another method that supports the evaluation of SpEL expressions, which can be used as follows:

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
            .antMatchers("/user", "/menu")
            .access("hasRole('ADMIN')")
            .antMatchers("/", "/**").permitAll();
    }Copy the code

It implements the same rules as the above method. Spring Security also provides other rich SpEL expressions, such as:

express describe
hasRole(String role) returntrueWhether the current client has a specified role. For example,hasRole('admin')By default, provided roles that do not start with “ROLE_” are added. You can modify itdefaultRolePrefixOn customizationDefaultWebSecurityExpressionHandler.
HasAnyRole (String... roles) returntrueWhether the current principal has any of the roles provided (in the form of a comma-separated list of strings). For example,hasAnyRole('admin', 'user')By default, provided roles that do not start with “ROLE_” are added. You can modify itdefaultRolePrefixOn customizationDefaultWebSecurityExpressionHandler.
hasAuthority(String authority) returntrueWhether the current client has designated authority. For example,hasAuthority('read')
HasAnyAuthority (String... authorities) returntrueIf the current body has any of the supplied authorities (given a comma-separated list of strings) for example,hasAnyAuthority('read', 'write')
principal Allows direct access to the principal object representing the current user
authentication Allow direct accessAuthenticationfromSecurityContext
permitAll Always evaluated astrue
denyAll Always evaluated asfalse
isAnonymous() returntrueWhether the current client is an anonymous user
isRememberMe() returntrueWhether the current subject is a Remember Me user
isAuthenticated() trueReturns if the user is not anonymous
isFullyAuthenticated() returntrueIf the user is not anonymous or remember my user
hasPermission(Object target, Object permission) returntrueWhether a user can access a given target for a given permission. For example,hasPermission(domainObject, 'read')
hasPermission(Object targetId, String targetType, Object permission) returntrueWhether a user can access a given target for a given permission. For example,hasPermission(1, 'com.example.domain.Message', 'read')
2. Login

If we have our own login interface and need to replace the default interface provided by Spring Security, we can use the fromLogin() and loginPage() methods to do this:

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
            .antMatchers("/user", "/menu")
            .access("hasRole('ADMIN')")
            .antMatchers("/", "/**").permitAll()
            .and()
            .formLogin()
            .loginPage("/login");
    }Copy the code

This points the login address to “/login”. To specify where to jump to if the login succeeds, use the defaultSuccessUrl() method:

           .and()
            .formLogin()
            .loginPage("/login")
            .defaultSuccessUrl("/home")Copy the code

After the user logs in, it will jump to the main page.

Next, let’s look at logout.

3. Log out

Similar to login, the logout() and logoutSuccessUrl() methods can be used to do this:

            .and()
            .logout()
            .logoutSuccessUrl("/login")Copy the code

In the preceding example, the login page is displayed after the user logs out.

4, summary

Now that we have a basic understanding of the Spring Security configuration, we can configure it as we want (basic). There is more Spring Security can do than just look at this article. The most effective way to learn it is to read the official documentation. Spring Security has the most up-to-date knowledge about Spring Security! IO /projects/sp…


Finally, recently, many friends asked me for Linux learning roadmap, so I stayed up for a month in my spare time according to my own experience, and sorted out an e-book. Whether you are interviewing or self-improvement, I believe will help you! The directory is as follows:

Free to everyone, just ask everyone to point to me!

Ebook | Linux development learning roadmap

Also hope to have a small partner can join me, do this e-book more perfect!

Have a harvest? Hope the old iron people come to a triple whammy, give more people to see this article

Recommended reading:

  • Dry goods | programmers advanced architect necessary resources free of charge
  • Artifact | support resource site search