Supporting code: gitee.com/lookouttheb…

The default security mechanism used in the previous chapter is one user, one role. In real development, we have many users, many roles, this chapter to implement multi-user authorization.

1. Resource preparation

Suppose we have two roles, one is administrator, with background operation rights, and the other is ordinary user, with website access rights.

1 the new controller

Create two controllers, AdminController and UserController. AdminController is for background administrator operations. In the UserController, users can perform operations only after logging in, such as changing passwords and profile pictures.

2 Resource Authorization

AntMatchers () takes the ANT pattern of the URL matcher. ANT mode use? Matches any single character, uses * to match 0 or any number of characters, and uses ** to match 0 or more directories. /admin/** indicates all requests under admin. HasRole (ADMIN) : only the ADMIN role can access it.

3 Restarting the Service

After the service is restarted, both /user/hello and /admin/hello cannot be accessed.

Added to the user in the configuration spring. Security. User. Roles =, after the user/user/hello can access

Memory – based multi-user support

Procedure 1 Modify the configuration file

We will delete the user from the configuration first. I am used to using yML, so the file will be changed to application-dev.ymlWe use -dev to distinguish the environment, so add the environment configuration to the startup:

The first step is to select Edit by clicking the drop down arrow, and then add the parameter: -dspring.profiles. Active =dev. The specified configuration is read when the project starts.

2 Implement a user-defined UserDetailsService

In the configuration class:

    @Bean
    public UserDetailsService userDetailService(a){
        InMemoryUserDetailsManager memory = new InMemoryUserDetailsManager();
        memory.createUser(User.withUsername("user").password("123").roles("USER").build());
        memory.createUser(User.withUsername("admin").password("123").roles("ADMIN").build());
        return memory;
    }

    @Bean
    public PasswordEncoder passwordEncoder(a){
        return new PasswordEncoder() {
            @Override
            public String encode(CharSequence rawPassword) {
                return rawPassword.toString();
            }

            @Override
            public boolean matches(CharSequence rawPassword, String encodedPassword) {
                returnencodedPassword.contentEquals(rawPassword); }}; }Copy the code

Customize the UserDetailsService in the configuration class instead of the default. Add two users.

Add a custom PasswordEncoder to verify passwords.

3 Start project verification

After startup, user login can only access user/ Hello. To access admin/hello, log in as the admin user.

Mysql – based multi-user support

The custom database structure is actually just a matter of implementing a custom UserDetailsService, letting Spring discover the injection, and then automatically doing the authentication and authorization for us.

The UserDetailsService defines only one loadUserByUsername method to get a UserDetails object. The UserDetails object contains a list of information used in authentication, including the user name, password, permissions, and other information, which SpringSecurity uses to determine whether the authentication was successful.

public interface UserDetails extends Serializable {
    Collection<? extends GrantedAuthority> getAuthorities();

    String getPassword(a);

    String getUsername(a);

    boolean isAccountNonExpired(a);

    boolean isAccountNonLocked(a);

    boolean isCredentialsNonExpired(a);

    boolean isEnabled(a);
}
Copy the code

That is, no matter what data source you use, as long as you implement the UserDetailsService and return the UserDetails.

1. Prepare the database

sql:

CREATE TABLE `users` (
    `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'primary key',
    `username` VARCHAR(50) NOT NULL UNIQUE DEFAULT ' ' COMMENT 'Username',
    `password` varchar(255) NOT NULL DEFAULT ' ' COMMENT 'password',
    `role` TINYINT(5) unsigned NOT NULL DEFAULT '0' COMMENT 'User type 0 Common user, 1 Administrator',
    `delete` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT 'Delete, 0 no, 1 yes',
    `createtime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'Creation time',
    `updatetime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'Update Time'.PRIMARY KEY (`id`),
    KEY `idx_createtime`(`createtime`),
    KEY `idx_username`(`username`)
)ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT ='User table';
Copy the code

Add connection configuration:

spring: datasource: url: jdbc:mysql://localhost:3306/novel? serverTimezone=GMT%2B8&autoReconnect=true&useSSL=false&failOverReadOnly=false
        username: root
        password: 123456
        driver-class-name: com.mysql.cj.jdbc.Driver
Copy the code

2. Encode UserDetails

@Data
public class BaseDO {

    private Long id;

    private String createTime;

    private String updateTime;
}

@Data
@Table(name = "users")
public class UsersDO extends BaseDO implements UserDetails {
    private String username;

    private String password;

    private Integer role;

    private Boolean enable;

    @Column(exist = false) // Indicates not a database field -- custom annotation
    private Set<GrantedAuthority> authorities;

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return this.authorities;
    }

    @Override
    public boolean isAccountNonExpired(a) {
        return true;
    }

    @Override
    public boolean isAccountNonLocked(a) {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired(a) {
        return true;
    }

    @Override
    public boolean isEnabled(a) {
        return this.enable; }}Copy the code

There are several methods that implement the UserDetails definition:

  • IsAccountNonExpired, isAccountNonLocked, and isCredentialsNonExpired are not needed for the time being, and all return true
  • IsEnabled corresponds to isDelete
  • The getAuthorities method itself corresponds to the ROLES field, but because the structure is inconsistent, it creates a new one here and fills it in later.

3. Database query method

Encapsulate Spring’s JdbcTemplate and write your own queries. In code gitee, given at the beginning of this article.

4. Create a UserDetailsService

@Repository
public class UserDao {
    @Resource
    private EasyJdbc easyJdbc;

    public UsersDO findUserByUserName(String username){
        String sql = "select * from users where username = :username limit 1";
        Map<String, String> param = new HashMap<>();
        param.put("username", username);
        returneasyJdbc.queryForObject(sql, param, UsersDO.class); }}@Service
public class UserServiceImpl implements UserDetailsService {
    @Resource
    private UserDao userDao;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        UsersDO userDD = userDao.findUserByUserName(username);
        if (userDD == null) {
            throw new UsernameNotFoundException("User does not exist");
        }
        userDD.setAuthorities(AuthorityUtil.generateAuthority(userDD.getRole()));
        returnuserDD; }}Copy the code

After querying UserDO in the service, you need to assign a value to authorities. Implement the AuthorityUtil class yourself and turn your role into a GrantedAuthority object.

public class AuthorityUtil {

    public static Set<GrantedAuthority> generateAuthority(Integer role){
        Set<GrantedAuthority> set = new HashSet<>();
        set.add(new SimpleGrantedAuthority(RoleEnum.getAuthority(role)));
        returnset; }}@Getter
@AllArgsConstructor
public enum RoleEnum {
    /** * user */
    USER(0."ROLE_USER"),
    ADMIN(1."ROLE_ADMIN"),;private final int role;

    private final String authority;

    public static String getAuthority(Integer role){
        for (RoleEnum roleEnum : RoleEnum.values()){
            if (roleEnum.role == role){
                returnroleEnum.authority; }}return null; }}Copy the code

The role must start with ROLE_ because the hasRole method in the configuration class determines that the role must start with ROLE_.

.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers("/user/**").hasRole("USER")
Copy the code

If you don’t want to start with ROLE_, you can call the hasAuthority() method instead.

Third, summary

1. Introduces the realization of multi-user authorization and authentication based on memory and database. Either way, the most important thing is to implement the UserDetailsService, which returns the UserDetails. SpringSecurity takes care of the rest. 2. This article uses the JdbcTemplate of Spring to check the database. If you are interested, you can check 3 in the gitee link at the beginning of this article. This article creates a number of new directories:

Welcome to leave comments and discuss