Spring Security offers a variety of options for this account system,

  1. Based on memory;

  2. Based on JDBC.

  3. Based on the LDAP;

  4. Customize services.

Either way, it is covered by WebSecurityConfigurerAdapter based configuration class defined in the configure () method.

1 Based on memory

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication().withUser("deniro")
                .password("2382")
                .authorities("ROLE_USER")
                .and()
                .withUser("echo")
                .password("2382")
                .authorities("ROLE_USER");
    }
}
Copy the code

Auth. InMemoryAuthentication () will return InMemoryUserDetailsManagerConfigurer object, the object has withUser () method, after each call will configure a user in the memory. The withUser() method takes in the account name, while the password and authorization information are specified through the password() and authorities() methods.

This method is easy to use, suitable for testing or simple applications, but not easy to maintain. Because it is hard coded, if you need to add, delete, or change accounts, you must change the code, then rebuild and deploy the application.

2 Based on JDBC

Typically, account information is stored in a database, which is where jDBC-based account configuration is used.

(1) Basic code

The basic code template is as follows:

    @Autowired
    DataSource dataSource;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.jdbcAuthentication()
                .dataSource(dataSource);
                
    }
Copy the code

The dataSource is initialized using an injection mode.

(2) Role-based permission system

By default, springframework security uses five tables of role-based permissions to store account, permissions, and role information.

As you can see from the relationship in the figure, permissions can be granted not only to a group but also to an account.

If you already have a permission system in your application, you can customize it. You can customize the following three SQL statements.

  1. According to the account query account (username, password, enabled);
  2. Query permission by account (username,authority);
  3. Query the group name and permission (id, group_name, authority) by account.

These statements can be seen in the jdbCdaoimp.java implementation SQL:

public static final String DEF_USERS_BY_USERNAME_QUERY = "select username,password,enabled " + "from users " + "where username = ?" ; public static final String DEF_AUTHORITIES_BY_USERNAME_QUERY = "select username,authority " + "from authorities " + "where username = ?" ; public static final String DEF_GROUP_AUTHORITIES_BY_USERNAME_QUERY = "select g.id, g.group_name, ga.authority " + "from groups g, group_members gm, group_authorities ga " + "where gm.username = ? " + "and g.id = ga.group_id " + "and g.id = gm.group_id";Copy the code

(3) User-defined authority system

JdbcUserDetailsManagerConfigurer object support: programming, we can through linking programming to customize the three statements, like:

 auth.jdbcAuthentication()
                .dataSource(dataSource)
                .usersByUsernameQuery(
                        "select username,password,enabled from Users where username=?"
                ).authoritiesByUsernameQuery(
                        "select username,authority from UserAuthorities where username=?"
        ).groupAuthoritiesByUsername("xxx");
Copy the code

Note: The queried field name must be the same as the default field name. Therefore, it is not completely flexible to customize, but must follow certain specifications.

(4) Encrypted storage

Generally speaking, for security reasons, the password in the database must be ciphertext. Spring Security has taken this into account. JdbcUserDetailsManagerConfigurer class defines a passwordEncoder () method, to accept the specified password encoder as the parameters. The call method is as follows:

auth.jdbcAuthentication()
          .dataSource(dataSource)
          ...
          .passwordEncoder(new StandardPasswordEncoder("xxx"));
Copy the code

The StandardPasswordEncoder class is no longer recommended in Spring Boot2.x because it is no longer safe. It doesn’t matter if you don’t use this class, because any class that implements the PasswordEncoder interface can be used as an input parameter to the PasswordEncoder () method.

The Security encryption module defines many implementation classes for the PasswordEncoder interface.

  1. BCryptPasswordEncoder: Use BCrypt encryption (recommended).
  2. NoOpPasswordEncoder: No transcoding is performed.
  3. Pbkdf2PasswordEncoder: Use PBKDF2 encryption (recommended).
  4. SCryptPasswordEncoder: Use SCrypt encryption (recommended).

Because unidirectional encryption is used, the user encrypts the entered password according to the specified algorithm and compares it with the password that has been transcoded in the database. This actually calls the matches() method of the PasswordEncoder interface. The method is defined as follows:

boolean matches(CharSequence rawPassword, String encodedPassword);
Copy the code

3 Based on LDAP

(1) Concept

LDAP is an open, neutral, and industry-standard application protocol that provides access control and maintains distributed directory information over IP.

A directory service is a special database that holds descriptive, attribute-based details and supports filtering. It is dynamic, flexible, and easily extensible. Things like people management, phone books, address books, etc., are better suited to provide services in the form of directories.

Unlike relational databases, directory databases have good read performance but poor write performance, and do not have complex functions such as transaction processing and rollback, so they are not suitable for storing frequently modified data. More suitable for application and query scenarios.

(2) Basic usage

Spring Security, AuthenticationManagerBuilder class defines ldapAuthentication support LDAP authentication () method.

Start by adding the spring-security-LDAP dependency package to pom.xml in your project:

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

If not properly introduced rely on, will be thrown under Caused by: Java. Lang. ClassNotFoundException: org.springframework.security.ldap.DefaultSpringSecurityContextSource

Then configure the LDAP server in the configure() method of the SecurityConfig class:

auth.ldapAuthentication()
              .userSearchFilter("(uid={0})")
              .groupSearchFilter("member={0}");
Copy the code

LdapAuthentication () method returns the LdapAuthenticationProviderConfigurer class, which defines the userSearchFilter () and groupSearchFilter () method, You can query accounts and groups.

By default, queries start at the root of the LDAP hierarchy. We can also specify the query starting point.

auth.ldapAuthentication()
              .userSearchBase("ou=apple")
              .userSearchFilter("(uid={0})")
              .groupSearchBase("ou=fruits")
              .groupSearchFilter("member={0}");
Copy the code

The userSearchBase() method specifies that the account is queried from an organization named Apple; The groupSearchBase() method specifies the group to start the query from an organization called FRUITS.

(3) Password comparison policy

The default LDAP authentication policy is binding, that is, accounts are directly authenticated through the LDAP server. You can also use a password comparison policy. To do this, you need to send the entered password to the LDAP directory, and the LDAP server compares the password with the account password. Because only the LDAP server holds the password of the account, the application does not know the real password of the account, which further improves the password security.

Changing to a passwordCompare policy is as simple as adding the.passwordcompare () method after auth.ldapauthentication () :

auth.ldapAuthentication() .userSearchBase("ou=apple") .userSearchFilter("(uid={0})") .groupSearchBase("ou=fruits") .groupSearchFilter("member={0}") // passwordCompare();Copy the code

By default, the entered password is compared with the userPassword attribute on the LDAP server. If the password is defined in another attribute, we can specify the name of the attribute that actually holds the password with the passwordAttribute() method.

auth.ldapAuthentication() .userSearchBase("ou=apple") .userSearchFilter("(uid={0})") .groupSearchBase("ou=fruits") PasswordCompare () passwordAttribute(" PWD "); groupSearchFilter("member={0}") passwordCompare() passwordAttribute(" PWD ");Copy the code

(4) Remote LDAP server

By default, Spring Security listens on port 33389 of this machine, assuming that the LDAP service is on the same machine as the application service. The remote LDAP server can be configured using the contextSource() method.

Because the contextSource() method returns the ContextSourceBuilder, it cannot be hung under the previous concatenated code fragment and must be redone.

LdapAuthenticationProviderConfigurer<AuthenticationManagerBuilder> configurer = auth.ldapAuthentication(); . // Specify the LDAP server configurer.contextsource ().url(" LDAP :// XXX: XXX ");Copy the code

(5) Embedded LDAP server

The embedded LDAP server can be started using the contextSource() root() method:

configurer.contextSource()
                .root("");
Copy the code

When you start the application, you will see in the log the URL of the local embedded LDAP server that was started:

When the LDAP server starts, it looks for LDIF files in the classpath to load data. LDAP Data Interchange Format (LDIF) is a Data Interchange Format used by LDAP servers.


There will be a separate article on how to customize the account system.