Super detailed, with source code! SSM + spingboot + Vue + Shiro is used to login, add, delete, check and modify.

Time:2021-2-26

Before I wrote a blog, I used SSM + thymeleaf + Vue + Shiro to complete a blog withPermission login, and can add, delete, check and modifyIt’s such a project. At that time, only the operation of login permission was recorded. Now I will addAdd, delete, check and modifyThe concrete realization of the system.

Last blog: using Shiro to realize login authority authentication
Detailed code description.

First of all, give the complete code, click GitHub connection to get it.

1、 Project requirements:

  1. The system needs to be developed under spring boot
  2. The database can use mybatis (can be mixed with JPA Development)
  3. The page uses the thymeleaf template and Vue.js (element UI can be used for vuejs components)
  4. The login permission is spring boot security or Shiro framework (choose one)
  5. The workload can not be lower than boot management system, the interface is beautiful and practical

Super detailed, with source code! SSM + spingboot + Vue + Shiro is used to login, add, delete, check and modify.

2、 Technology stack used:

  1. Database: springboot, springmvc, mybatis
  2. Page: thymeleaf, semantic, Vue, bootstrap, Axios
  3. Login rights and authentication: Shiro

3、 Development process:

1. General development process:

  • Login: authentication, authorization
  • User information: add, delete, check and modify

2. Details:

2.1 using Shiro security framework:

(1) First, write our underlying database SQL statement and persistence layer entity class

We need three tables, user, role and permission, to define their role in login. First, we need to query whether there is a person in the user table by name and password. After the query, we use the user’s foreign key role_ ID to map the person’s role, and then use the ID in the role table to query the permissions in the permission table corresponding to the role. In this way, we will complete the operation of this person’s identity and permissions, and jump to the home page index.html , the home page will display the corresponding operable events according to the permissions, for example:User has the right to query. Based on user, admin can modify and add user information_ Admin can be deleted.We need to know the relationship between the three entity classes, user and role one-to-one, role and permissions one-to-one. Of course, we can also write it as many to many. This requires changing the database file and the entity class.
The persistence layer can use Lombok, so that we can reduce the code amount of persistent classes and the writing of set and get.

SQL code:

-- ----------------------------
-- Table structure for role
-- ----------------------------
CREATE TABLE `role`  (
  `id` int(11) NOT NULL AUTO_ Create comment 'role table primary key',
  `role_ Name ` varchar (32) default null comment 'role name',
  PRIMARY KEY (`id`)
);

-- ----------------------------
-- Records of role
-- ----------------------------
INSERT INTO `role` VALUES (1, 'SUPER_ADMIN');
INSERT INTO `role` VALUES (2, 'ADMIN');
INSERT INTO `role` VALUES (3, 'USER');

-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user`  (
  `id` int(11) NOT NULL AUTO_ Create comment 'user primary key',
  `User name ` varchar (32) not null comment 'user name',
  `Password ` varchar (32) not null comment 'password',
  `role_ ID ` int (11) default null comment 'foreign key associated with role table',
  PRIMARY KEY (`id`),
  CONSTRAINT `user_role_on_role_id` FOREIGN KEY (`role_id`) REFERENCES `role` (`id`)
);

-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES (1, 'BWH_Steven', '666666', 1);
INSERT INTO `user` VALUES (2, 'admin', '666666', 2);
INSERT INTO `user` VALUES (3, 'zhangsan', '666666', 3);

-- ----------------------------
-- Table structure for permission
-- ----------------------------
CREATE TABLE `permission`  (
  `id` int(11) NOT NULL AUTO_ Create comment 'primary key of permission table',
  `permission_ Name ` varchar (50) not null comment 'permission name',
  `role_ ID ` int (11) default null comment 'foreign key associated with role table',
  PRIMARY KEY (`id`),
  CONSTRAINT `permission_role_on_role_id` FOREIGN KEY (`role_id`) REFERENCES `role` (`id`)
);

-- ----------------------------
-- Records of permission
-- ----------------------------
INSERT INTO `permission` VALUES (1, 'user:*', 1);
INSERT INTO `permission` VALUES (2, 'user:*', 2);
INSERT INTO `permission` VALUES (3, 'user:queryAll', 3);
  • The entity layer is relatively simple, with no code attached
(2) And then there’s that pom.xml Add related dependencies. No code will be pasted here. The required GitHub will be taken by itself.
(3) Integration of mybatis and springboot:

You just need to create a Dao layer and a service layer. You need to remember to add annotations and be clear about their corresponding relationship

① Mapper configuration file (also in the form of annotation)

UserMapper.xml code

<?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="com.example.csy.dao.UserMapper">

    <select id="queryUserByUsername" resultMap="userRoleMap">
        SELECT u.*,r.role_name FROM `user` u, `role` r
          WHERE username = #{username} AND u.role_id = r.id;
    </select>
    <! -- define the resultmap encapsulating user and role -- >
    <resultMap id="userRoleMap" type="com.example.csy.entity.User">
        <id property="id" column="id"/>
        <result property="username" column="username"></result>
        <result property="password" column="password"></result>
        <result property="roleId" column="role_id"></result>
        <! -- configure and encapsulate the content of userpojo -- >
        <association property="role" javaType="com.example.csy.entity.Role">
            <id property="id" column="id"></id>
            <result property="roleName" column="role_name"></result>
        </association>
    </resultMap>


    <select id="queryPermissionByUsername" resultMap="permissionRoleMap">
        SELECT p.* ,r.role_name FROM `user` u, `role` r, `permission` p
          WHERE username = #{username} AND u.role_id = r.id AND p.role_id = r.id;
    </select>
    <! -- define the resultmap that encapsulates permission and role -- >
    <resultMap id="permissionRoleMap" type="com.example.csy.entity.Permissions">
        <id property="id" column="id"/>
        <result property="permissionName" column="permission_name"></result>
        <result property="roleId" column="role_id"></result>
        <! -- configure the content of encapsulated role -- >
        <association property="role" javaType="com.example.csy.entity.Role">
            <id property="id" column="id"></id>
            <! -- property is the parameter name assigned in the entity class, and column is the column name of the database -- >
            <result property="roleName" column="role_name"></result>
        </association>
    </resultMap>
</mapper>

② Dao layer and service are also relatively simple.

③ At this point, our mybatis + springboot integration is basically over, so let’s test it in the test class

@SpringBootTest
class CsyApplicationTests {

    @Autowired
    private UserMapper userMapper;

    @Test
    void contextLoads() {
        User admin = userMapper.queryUserByUsername("admin");
        System.out.println(admin.toString());
        Permissions permission = userMapper.queryPermissionByUsername("admin");
        System.out.println(permission.toString());
    }
}
(4) Integrate Shiro into the project:

① The key of. Shiro is the realm component:

It can be understood that when the communicator and intermediate bridge between Shiro and data are authorized, they will go to this part to find some content. In essence, realm is a security platform that has undergone a lot of encapsulation Dao, this is the introduction of the official website. In my understanding, the real component is actually a Dao. We define two methods here, one is identity authentication dogetauthenticationinfo, and the other is authorization dogetauthorizationinfo. These two methods are very important, so we give all the codes:

*Userrealm Code:

public class UserRealm extends AuthorizingRealm {

    @Autowired
    private UserMapper userMapper;
    /**
     *@ methodname @ dogetauthorizationinfo authorization operation
     *@ description permission configuration class
     * @Param [principalCollection]
     * @Return AuthorizationInfo
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        //Get user name information
        String username = (String) principalCollection.getPrimaryPrincipal();
        //Create a simple authorization verification message
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        //Set the role information obtained from the role table for this user
        authorizationInfo.addRole(userMapper.queryUserByUsername(username).getRole().getRoleName());
        //Set the permission information obtained from the permission table for this user
        authorizationInfo.addStringPermission(userMapper.queryPermissionByUsername(username).getPermissionName());
        return authorizationInfo;
    }
    /**
     *@ methodname @ dogetauthenticationinfo authentication
     *@ description authentication configuration class
     * @Param [authenticationToken]
     * @Return AuthenticationInfo
     * @Author WangShiLin
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        //Get the user name according to the token created in the receiving foreground data
        String username = (String) authenticationToken.getPrincipal();
        //Query related user information (entity) through user name
        User user = userMapper.queryUserByUsername(username);
        if (user != null) {
            //Session, optional
            SecurityUtils.getSubject().getSession().setAttribute("user", user);
            //The work of password authentication should be done by Shiro
            AuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(), "userRealm");
            return authenticationInfo;
        } else {
            //Return null to throw an exception
            return null;
        }
    }
}

② Next, write the shiroconfig annotation class

Inject all the classes and attributes into the spring container, but we use springboot and use the annotation: @ configuration directly.
This annotation class is mainly used to inject two custom methods of realm class and configure security manager There are also security manager and filter. This filter is a key method to assign permissions. For this filter, Shiro has an encapsulated shirofilterfactorybean factory class. It has many custom methods, such as set login page, success page, unauthorized page, etc., which can be controlled by the controller of springmvc.
Let’s focus on the interception and release (map): store in the form of map key value pairs, key stores URL, value stores corresponding permissions or roles, etc. in fact, the key is very easy to understand, such as / CSS/、/user/admin/It represents all the files in the CSS folder, and the prefix of the request path is / user / admin / url, and the corresponding value has a certain specification.
For example, some of Shiro’s own permissions are as follows

**Key:
Anon: you can visit it without authentication, that is, tourists can also visit it
Authc: authentication is required to access, that is, after logging in
Roles [XXX]: for example, it can only be accessed with a certain role identity. Note: XXX is a role parameter
Perms [XXX]: you must have permission to access a request or resource. Note: XXX is a permission parameter**

For example, all the data in our own pictures and static files can be directly released by anon without management.

All codes of shiroconfig configuration class:

@Configuration
public class ShiroConfig {

    //Add your own verification method to the container
    @Bean
    public UserRealm myShiroRealm() {
        return new UserRealm();
    }
    /**
     *Configure security manager - securitymanager
     *
     * @return
     */
    @Bean
    public DefaultWebSecurityManager securityManager() {
        //Add custom "realm"
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        //Real
        securityManager.setRealm(myShiroRealm());
        return securityManager;
    }
    /**
     *Configure the Shiro filter
     *
     * @param securityManager
     * @return
     */
    @Bean
    public ShiroFilterFactoryBean shiroFilter(DefaultWebSecurityManager securityManager) {
        //Define shirofactorybean
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        //Associate with security manager
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        //Customize the login page. If you log in, you will execute this request, that is, jump to the login page
        shiroFilterFactoryBean.setLoginUrl("/toLoginPage");
        //Specify success page
        shiroFilterFactoryBean.setSuccessUrl("/success");
        //Specify unauthorized interface
        shiroFilterFactoryBean.setUnauthorizedUrl("/unauthorized");
        //Set custom filter
        Map<String, Filter> filterMap = new LinkedHashMap<>();
        filterMap.put("anyRoleFilter", new MyRolesAuthorizationFilter());
        shiroFilterFactoryBean.setFilters(filterMap);
        //LinkedHashMap is ordered, and can be configured as sequential interceptor
        Map<String, String> filterChainMap = new LinkedHashMap<>();
        //Configure the address that can be accessed anonymously, add it according to the actual situation, release some static resources, etc. anon means release
        filterChainMap.put("/css/**", "anon");
        filterChainMap.put("/img/**", "anon");
        filterChainMap.put("/js/**", "anon");
        //Specify page release, such as login page to allow everyone to login
        filterChainMap.put("/toLoginPage", "anon");
        //Users beginning with "/ user / Admin" need to be authenticated, and authc means to be authenticated
        filterChainMap.put("/user/admin/**", "authc");
        //Page - user needs role authentication
//        filterChainMap.put("/levelA/**", "anyRoleFilter[USER,ADMIN,SUPER_ADMIN]");
        filterChainMap.put("/levelB/**", "anyRoleFilter[ADMIN,SUPER_ADMIN]");
//        filterChainMap.put("/levelC/**", "anyRoleFilter[SUPER_ADMIN]");
//        filterChainMap.put("/levelA/**", "roles[USER]");
//        filterChainMap.put("/levelB/**", "roles[ADMIN]");
//        filterChainMap.put("/levelC/**", "roles[SUPER_ADMIN]");
        //All requests under / user / admin / must be authenticated. Only those with the permission of user: [*] can be accessed, or they can be set to user:xxx
        filterChainMap.put("/user/admin/**", "perms[user:*]");
        //Configure logout filter
        filterChainMap.put("/logout", "logout");
        //Save map to filter
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainMap);
        return shiroFilterFactoryBean;
    }
    /**
     *? thymeleaf
     * @return
     */
    @Bean(name = "shiroDialect")
    public ShiroDialect shiroDialect(){
        return new ShiroDialect();
    }
}

④ To customize a role authentication filter myrolesauthorizationfilter:

Because our role only needs to have one role to access the mapping page, Shiro is hasallroles by default, that is to say, we need to satisfy all identities to access, so we need to customize a hasanyroles and choose any role.

public class MyRolesAuthorizationFilter extends AuthorizationFilter {
    @SuppressWarnings({"unchecked"})
    public boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws IOException {
        Subject subject = getSubject(request, response);
        String[] rolesArray = (String[]) mappedValue;
        if (rolesArray == null || rolesArray.length == 0) {
            //no roles specified, so nothing to check - allow access.
            return false;
        }
        List<String> roles = CollectionUtils.asList(rolesArray);
        boolean[] hasRoles = subject.hasRoles(roles);
        for (boolean hasRole : hasRoles) {
            if (hasRole) {
                return true;
            }
        }
        return false;
    }
}
(5) Integration of thymeleaf and semantic:

UI we use semantic, which is OK: next, we will show the page effect directly
We limit the permissions of various roles to the operation of the database. As mentioned at the beginning, user has the permission to query, and admin can modify and add user information on the basis of user_ Admin can be deleted.
We implement this operation by setting some roles on some labels of the table
The key code is the declaration of the tag in the index home page

A. User has the right to query
shiro:hasAnyRoles=”SUPER_ADMIN,ADMIN,USER”

B. Admin can be modified on the basis of user
shiro:hasAnyRoles=”SUPER_ADMIN,ADMIN”

C.SUPER_ Admin can be deleted on the admin permission
shiro:hasAnyRoles=”SUPER_ADMIN”

(6) Finally is the controller, because it is relatively simple, do not write code.
(7) Shiro effect display:

Sign in login.html

Super detailed, with source code! SSM + spingboot + Vue + Shiro is used to login, add, delete, check and modify.

User permission:

Super detailed, with source code! SSM + spingboot + Vue + Shiro is used to login, add, delete, check and modify.

Admin permission:

Super detailed, with source code! SSM + spingboot + Vue + Shiro is used to login, add, delete, check and modify.

SUPER_ Admin permission:

Super detailed, with source code! SSM + spingboot + Vue + Shiro is used to login, add, delete, check and modify.

So far, Shiro’s login authentication and permission assignment operations have been completed. The next step is to add, delete, check and modify user information

2.2 add, delete, check and modify user information

(1) The writing of Dao (mapper) layer and service layer

Due to the use of mybatis, we need to have two ways, one is to use annotations, the other is to write configuration files. In the project, both ways are used.
For two one to many queries, the configuration file is used. For other additions, deletions, and modifications, the annotation form is used. Because the mapper class is relatively simple, the code is not pasted. There is another one UserMapper.xml The code of the configuration file has been posted in Shiro, and it is not written here.
Then we write the service class. We use the @ service annotation. Both the interface and the implementation class need the @ service annotation. Then we call the method through mapper in the implementation class, and add some restrictions, such as:
When adding user information, if the user exists, the printing fails and the method ends directly.

(2) Write test class:

After writing the test, if there is an error, we don’t have to worry about the error of Dao layer and service.

@Test
    void queryAll(){
        List<User> users = userService.queryAll();
        System.out.println(users);
    }

    @Test
    void contextLoads() {
        User admin = userService.queryUserByUsername("admin");
        System.out.println(admin.toString());
        Permissions permission = userService.queryPermissionByUsername("admin");
        System.out.println(permission.toString());
    }
    @Test
    void addTest() {
        User user = new User("wu","123",3);
        int i = userService.addUser(user);
        System.out.println(i);
    }
    @Test
    void deleteTest() {
        userService.deleteUser("zhangsan");
    }
    @Test
    void updateTest() {
        User user = new User("ww","123",3);
    }
}

(3) Display data to home page index.html Above:

We mainly use Vue and Axios to display the data to the page. We use Vue’s instructions, such as V-for, V-model, and so on. The UI uses semantic and bootstrapData operation is divided into four stepsHow to achieve it.

A. Query all:

In the definition of mapper, we have a queryall() method. In the controller, we return a list < user >, so getting the list in the index is the most critical problem. Here, we directly define the Vue object in the index page, VAR VM = new vue(); define an ID for the data display table, which is represented by Vue.
Just define an array userlist in the data attribute to assign the list data queried by queryall to the userlist array.
Of course, according to our general experience, the query method should be displayed when we log in, so Axios plays a role. It allows us to display the data on the page without refreshing, and it is more convenient to use Axios in Vue.
We need to consider how long query operations should be displayed. We know that Vue objects use declaration cycles. We can call the queryall method after creating Vue objects, so that we can query all operations as soon as we log in.
Here, we only need to add two methods to the Vue object, one is queryall and the other is create. It should be noted that when we assign a value to userlist, we need to declare a variable var_ This = this; because the Vue object is different from Axios, the object userlist in Vue will be called only when this is assigned, otherwise the Axios this will be called. Of course, we did not write it, so if we write it like this, we will find that there is no data.
After assigning the value of userlist, you only need to show it, use the V-for instruction, and the value symbol.

Vue code section:

    var vm = new Vue({
        el: "#app",
        data: {
            user: {id:"",
                username:"",
                password:"",
                roleId:"",
                role:{id:"",roleName:""}
                },
            userList: [],
            wo:"ri"
        },
        methods: {
            addUser:function (user) {
                var _this = this;
                axios.post("/user/admin/add",_this.user).then(function (response) {
                    _this.queryAll();
                    console.log(_this.userList);
                }).catch(function (err) {
                    console.log(_this.user);
                    console.log(err);
                });
            },
            deleteUser:function (username) {
                var _this = this;
                axios.post("/user/admin/delete",username=username).then(function (response) {
                    _this.queryAll();
                }).catch(function (err) {
                });
            },
            queryAll: function () {
                var _this = this;
                axios.get("/user/queryAll").then(function (response) {
                    _this.userList = response.data;
                    console.log(_this.userList);
                }).catch(function (err) {
                    console.log(err);
                });
            },
            queryByName: function (username) {
                var _this = this;
                axios.get("/user/queryByName", {
                    params: {
                        username: username
                    }
                }).then(function (response) {
                    _this.user = response.data;
                    $('#updateModal').modal("show");
                    console.log(_this.user);
                }).catch(function (err) {
                    console.log(err);
                });
            },
            updateUser: function (user) {
                var _this = this;
                axios.post("/user/admin/update",_this.user).then(function (response) {
                    _this.queryAll();
                    console.log(_this.user);
                }).catch(function (err) {
                });
            }
        },
        created(){
            this.queryAll();
        }
    });

HTML table that part of the code is relatively simple, do not paste.

B. Delete user:

For deleting a user, we can get a TD after each query result, so that we can get the ID of the user in the current row, which is convenient for assignment. Here we use v-on: to directly call the method deleteuser (ID) defined in Vue, which we also use here axios.post (), because we need to pass parameters and perform operations. The code of the deleteuser method is just above We need to query all the methods queryall directly after the operation, so we need to call queryall after the delete_ this.queryAll () method. This is more interactive.

C. Modify user:

To modify users, we can use the modal box in bootstrap. The advantage of using modal box is that it has good interactivity, does not need to jump to the page, and is more concise to use. It’s not very difficult to use the modal box. Just use the bootstrap syntax in the class in the div tag. After clicking modify, the querybyname method will be called. In the method, the value symbol will be used to get the ID of the modal box, and the modal (“show) method encapsulated by bootstrap will be called to display the modal box.
_this.user = response.data;$(‘#updateModal’).modal(“show”);

E. Add users:

Adding users is almost the same as modifying users. It’s also a V-model implementation, only with a prompt. Of course, the follow-up operation is the same as above. It’s also a direct queryall (). Another point is that when the data is sent to us from a web page, JSON needs to be converted into a string by using @ requestbody in the controller. If there is a return value, it also needs to use @ ResponseBody. Because you want to assign values.

Super detailed, with source code! SSM + spingboot + Vue + Shiro is used to login, add, delete, check and modify.
This is the end of the development.

4、 The experimental results show that:

The effect of the implementation you can clone down the source code to test, here I only give some general display.
(1) Add users:
Super detailed, with source code! SSM + spingboot + Vue + Shiro is used to login, add, delete, check and modify.

The content of this blog basically ends here. If you think it’s useful, you know, it’s not easy to code! Hee hee!

Recommended Today

JavaScript process control

(1) Branches If with else if, else Switch with case and default[difference] ① the comparison range of the former is written according to ‘bubble sort’, with more branches; ② The latter satisfies a specific value, has fewer branches and can be interrupted, and has higher execution efficiency. The value of ‘case’ is congruent (2) Cycle […]