An article takes you through the spring security database based authentication (spring security integration mybatis)

1, Pre configuration

1. Join dependency

<dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.10</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
            <version>5.1.27</version>
        </dependency>

  

 

The MySQL connector Java connection version selected here is 5.1.27, corresponding to application Properties are:

spring.datasource.url=jdbc:mysql://localhost:3306/yolo
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.username=root
spring.datasource.password=root

  

 

If it is version 8.0 or above, then the application Properties , spring datasource. URL: serverTimezone needs to be added

spring.datasource.username=root
spring.datasource.password=root
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.url=jdbc:mysql://localhost:3306/security?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai

  

 

2. Database script

SET FOREIGN_KEY_CHECKS=0;

-- ----------------------------
-- Table structure for role
-- ----------------------------
DROP TABLE IF EXISTS `role`;
CREATE TABLE `role` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(32) DEFAULT NULL,
  `nameZh` varchar(32) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of role
-- ----------------------------
INSERT INTO `role` VALUES ('1', 'dba', 'Database Administrator');
INSERT INTO `role` VALUES ('2', 'admin', 'system administrator');
INSERT INTO `role` VALUES ('3', 'user', 'user');

-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(32) DEFAULT NULL,
  `password` varchar(255) DEFAULT NULL,
  `enabled` tinyint(1) DEFAULT NULL,
  `locked` tinyint(1) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES ('1', 'root', '$2a$10$RMuFXGQ5AtH4wOvkUqyvuecpqUSeoxZYqilXzbz50dceRsga.WYiq', '1', '0');
INSERT INTO `user` VALUES ('2', 'admin', '$2a$10$RMuFXGQ5AtH4wOvkUqyvuecpqUSeoxZYqilXzbz50dceRsga.WYiq', '1', '0');
INSERT INTO `user` VALUES ('3', 'sang', '$2a$10$RMuFXGQ5AtH4wOvkUqyvuecpqUSeoxZYqilXzbz50dceRsga.WYiq', '1', '0');

-- ----------------------------
-- Table structure for user_role
-- ----------------------------
DROP TABLE IF EXISTS `user_role`;
CREATE TABLE `user_role` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `uid` int(11) DEFAULT NULL,
  `rid` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of user_role
-- ----------------------------
INSERT INTO `user_role` VALUES ('1', '1', '1');
INSERT INTO `user_role` VALUES ('2', '1', '2');
INSERT INTO `user_role` VALUES ('3', '2', '2');
INSERT INTO `user_role` VALUES ('4', '3', '3');
SET FOREIGN_KEY_CHECKS=1;

 

2, Define entity classes

1. Define User

The entity class User needs to implement the UserDetails interface, because everyone can define the User object, but the User object defined by each person is different, which makes the attributes set by each person different. When the system needs to determine the login status of the User, it is impossible to determine which User to call because of the various settings of the User name and password

Therefore, all entity classes are required to implement the UserDetails interface, which is equivalent to a specification that defines the attribute name required for login verification, so all entity classes should comply with this specification.

public class User implements UserDetails {
    private Integer id;
    private String username;
    private String password;
    private Boolean enabled;
    private Boolean locked;
    private List<Role> roles;
    public List<Role> getRoles() {
        return roles;
    }
    public void setRoles(List<Role> roles) {
        this.roles = roles;
    }
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    @Override
    public String getUsername() {
        return username;
    }
    @Override
    public boolean isAccountNonExpired() {
        return true;
    }
    @Override
    public boolean isAccountNonLocked() {
        return !locked;
    }
    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }
    @Override
    public boolean isEnabled() {
        return enabled;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        List<SimpleGrantedAuthority> authorities = new ArrayList<>();
        for (Role role : roles) {
            authorities.add(new SimpleGrantedAuthority("ROLE_"+role.getName()));
        }
        return authorities;
    }
    @Override
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    public void setEnabled(Boolean enabled) {
        this.enabled = enabled;
    }
    public void setLocked(Boolean locked) {
        this.locked = locked;
    }
}

  

 

(1) accountNonExpired, accountNonLocked, credentialsNonExpired and enabled are used to describe the user's status respectively, indicating whether the account has not expired, whether the account has not been locked, whether the password has not expired and whether the account is available.
(2) The roles attribute indicates the User's Role. The User and Role are many to many relationships, which are described with an @ ManyToMany annotation.
(3) The getAuthorities method returns the user's Role information. In this method, we can convert our Role slightly.

(1) This collection returns all the roles of the user. Because the roles obtained from the database are roles, but a collection of getAuthorities is required, so it needs to be processed.

  @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        List<SimpleGrantedAuthority> authorities = new ArrayList<>();
        for (Role role : roles) {
            authorities.add(new SimpleGrantedAuthority("ROLE_"+role.getName()));
        }
        return authorities;
    }

 

Note here that there is a requirement for the authentication of the springsecurity ROLE, which must be based on ROLE_ Start, otherwise problems will occur:

authorities.add(new SimpleGrantedAuthority("ROLE_"+role.getName()));

 

Of course, when you add users to the data, you can also let the users use {ROLE_ Start, so you don't have to add it again

(2) In addition, you should pay attention to whether the account is not locked. The following information is stored in the database:


So here is the inverse of locked

@Override
    public boolean isAccountNonLocked() {
        return !locked;
    }

2. Define Role

public class Role {
    private Integer id;
    private String name;
    private String nameZh;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getNameZh() {
        return nameZh;
    }

    public void setNameZh(String nameZh) {
        this.nameZh = nameZh;
    }
}

  

3, Define Service

@Service
public class UserService implements UserDetailsService {
    @Autowired
    UserMapper userMapper;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userMapper.loadUserByUsername(username);
        if (user == null) {
            throw new UsernameNotFoundException("user does not exist!");
        }
        user.setRoles(userMapper.getUserRolesById(user.getId()));
        return user;
    }
}

  

The user service defined by ourselves needs to implement the user detailsservice interface. To implement this interface, we need to implement the method in the interface, that is, loadUserByUsername. The parameter of this method is the user name passed in by the user when logging in, and query the user information according to the user name (after finding it, the system will automatically compare the password).

4, Define Mapper

1. UserMapper

@Mapper
public interface UserMapper {
    User loadUserByUsername(String username);

    List<Role> getUserRolesById(Integer id);
}

  

2. UserMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.javaboy.securitydb.mapper.UserMapper">
    <select id="loadUserByUsername" resultType="org.yolo.securitymybatis.bean.User">
        select * from user where username=#{username}
    </select>

    <select id="getUserRolesById" resultType="org.yolo.securitymybatis.bean.Role">
        select * from role where id in (select rid from user_role where uid=#{id})
    </select>
</mapper>

 

Mapper location:


So you need to add a resource path: add the resource configuration path to the pom file

<resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.xml</include>
                </includes>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
            </resource>
</resources>

 

5, Define SecurityConfig

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    UserService userService;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userService);
    }

    @Bean
    PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
    @Bean
    RoleHierarchy roleHierarchy() {
        RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl();
        String hierarchy = "ROLE_dba > ROLE_admin \n ROLE_admin > ROLE_user";
        roleHierarchy.setHierarchy(hierarchy);
        return roleHierarchy;
    }
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/dba/**").hasRole("dba")
                .antMatchers("/admin/**").hasRole("admin")
                .antMatchers("/user/**").hasRole("user")
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .permitAll()
                .and()
                .csrf().disable();
    }
}

  

Test access: successful

 

After reading three things ❤️

If you think this article is very helpful to you, I'd like to invite you to help me with three small things:

  1. Like, forward, and have your "praise and comments" is the driving force of my creation.

  2. Follow the official account "java rotten pig skin" and share original knowledge from time to time.

  3. At the same time, we can look forward to the follow-up articles 🚀

Tags: Java

Posted by nels on Thu, 12 May 2022 20:10:14 +0300