springboot Learning-Configuration of Spring Security

Security is undoubtedly very important in web application development. Choosing Spring Security to protect web applications is a very good choice.Spring Security is one of the security modules in the Spring project, and it is very convenient to seamlessly integrate with the Spring project.Especially in the spring boot project, it is very simple to include spring security.In this article, we describe spring security and its use in Web applications.

Official website: https://spring.io/projects/spring-security

Spring Security is a powerful and highly customizable authentication and access-control framework. It is the de-facto standard for securing Spring-based applications.

Spring Security is a framework that focuses on providing both authentication and authorization to Java applications. Like all Spring projects, the real power of Spring Security is found in how easily it can be extended to meet custom requirements

In practice, users'basic information and roles are stored in the database, so data authentication is required in the database.

This article will show you how to authenticate and authorize using data obtained in the database.

1. Design the database

First, you need to design a basic user role table.

user
id int(11) Primary key; self-increasing
username varchar(32) DEFAULT NULL
password varchar(255) DEFAULT NULL
enabled tinyint(1) DEFAULT NULL
locked tinyint(1) DEFAULT NULL
role
id int(11) Primary key; self-increasing
name varchar(32) DEFAULT NULL
nameZh varchar(32) DEFAULT NULL
user_role
id int(11) Primary key; self-increasing
uid int(11) DEFAULT NULL
rid int(11) DEFAULT NULL
/*
Navicat MySQL Data Transfer

Source Server         : localhost_3306
Source Server Version : 50625
Source Host           : localhost:3306
Source Database       : security

Target Server Type    : MYSQL
Target Server Version : 50625
File Encoding         : 65001

Date: 2020-02-02 11:10:08
*/

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');

2. Create a project

Using mybatis, create a spring boot web project that adds the following dependencies:

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.3.2</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.10</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <scope>test</scope>
        </dependency>

3. Configure the database

Configure the database connection in application.properties:

spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.username=root
spring.datasource.password=abcdABCD123
spring.datasource.url=jdbc:mysql:///security

4. Create entity classes

Create entity classes corresponding to the role table and the user table, respectively, with the following code:

public class Role {
    private Integer id;
    private String name;
    private String nameZh;
    //Omit getter/setter

    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;
    }
}
public class User implements UserDetails {
    private Integer id;
    private String username;
    private String password;
    private Boolean enabled;
    private Boolean locked;
    private List<Role> roles;
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        List<SimpleGrantedAuthority> authorities = new ArrayList<>();
        for (Role role : roles) {
            authorities.add(new SimpleGrantedAuthority(role.getName()));
        }
        return authorities;
    }
    @Override
    public String getPassword() {
        return password;
    }
    @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;
    }
    //Omit getter/setter

    public Integer getId() {
        return id;
    }

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

    public void setUsername(String username) {
        this.username = username;
    }

    public void setPassword(String password) {
        this.password = password;
    }

//    public Boolean getEnabled() {
//        return enabled;
//    }

    public void setEnabled(Boolean enabled) {
        this.enabled = enabled;
    }

    public Boolean getLocked() {
        return locked;
    }

    public void setLocked(Boolean locked) {
        this.locked = locked;
    }

    public List<Role> getRoles() {
        return roles;
    }

    public void setRoles(List<Role> roles) {
        this.roles = roles;
    }
}

Code explanation:

4.1 User entity classes need to implement the UserDetails interface and implement seven methods within that interface

Method Name explain
getAuthorities(); Get role information for the current user object
getPassword(); Get the password for the current user object
getUsername(); Gets the user name of the current object
isAccountNonExpired(); Is the current account not expired
isAccountNonLocked(); Is the current account unlocked
isCredentialsNonExpired(); Is the current account password not expired
isEnabled(); Is the current account available

4.2 The user sets the return values of these seven methods according to the actual situation.Because by default, developers do not need to compare information such as password roles themselves, they only need to provide relevant information, such as the password returned by the getPassword() method does not match the login password entered by the user, the BadCredentialsException exception is automatically run out, the isAccountNonExpired() method returns false, and the AccountExpiredException exception is thrown automatically.So for developers, you just need to return the corresponding configuration here for the data in the database.In this case, because there are only enabled and locked fields in the database, both methods return true for Account Not Expired and Password Not Expired.

The 4.3getAuthorities() method is used to obtain information about the roles the current user has. In this case, the roles the user has are stored in the roles property, so the method traverses the roles property directly, then constructs the SimpleGrandAuthorities collection and returns it.

5. Create UserService

UserMapper and UserMapper.xml

@Mapper
public interface UserMapper {
    User loadUserByUsername(String username);
    List<Role> getUserRolesByUid(Integer id);
}
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.sang.security02.mapper.UserMapper">
    <select id="loadUserByUsername" resultType="org.sang.security02.model.User">
        select * from user where username=#{username}
    </select>
    <select id="getUserRolesByUid" resultType="org.sang.security02.model.Role">
        select * from role r,user_role ur where r.id=ur.rid and ur.uid=#{id}
    </select>
</mapper>

Next, create the UserService with the following code:

@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("Account does not exist!");
        }
        user.setRoles(userMapper.getUserRolesByUid(user.getId()));
        return user;
    }
}

Code explanation:

* Define UserService to implement the UserDetailsService interface, and implement the loadUserByUsername method in this interface. The parameters of this method are the user name entered by the user at logon time, find the user in the database by the user name, throw an exception that does not exist in the account if the user is not found, and continue to find the angle that the user has if the user is foundThe color information is returned, and the obtained user object is then compared by the DaoAuthenticationProvide class provided by the system to see if the password is correct.

* The loadUserByUsername method is called automatically when the user logs in.

6. Configure Spring Security

Next, configure Spring Security with the following code:

@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    UserService userService;
    @Bean
    RoleHierarchy roleHierarchy() {
        RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl();
        String hierarchy = "ROLE_dba > ROLE_admin ROLE_admin > ROLE_user";
        roleHierarchy.setHierarchy(hierarchy);
        return roleHierarchy;
    }
    @Bean
    PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userService);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
                    @Override
                    public <O extends FilterSecurityInterceptor> O postProcess(O object) {
                        object.setSecurityMetadataSource(cfisms());
                        object.setAccessDecisionManager(cadm());
                        return object;
                    }
                })
                .and()
                .formLogin()
                .loginProcessingUrl("/login").permitAll()
                .and()
                .csrf().disable();
    }
}

Next, create a Controller to test.

 

55 original articles published. 119 won. 340,000 visits+
Private letter follow

Tags: Spring Database Mybatis MySQL

Posted on Sat, 01 Feb 2020 21:27:50 -0800 by saf