A, Shiro

Shiro, which means Fortress, is an authentication and permission verification framework. Permissions are generally an essential part of a management system. Everyone in the company operates on the same system, but not everyone has the same rights. Then it is necessary to clearly identify the permissions of each person in the system and verify them in the back end. The permission system is divided into: button level permission (determines what a user can and cannot do) Data level permission (determines what data a user can do on the premise that the user can do this) The permission system is divided into two steps: 1. Wysiwyg is WYSIWYG We need to present the menus and buttons that the user has to the user at the interface level of the system. Menus and buttons that are not available will not be displayed. 2. Backend permission verification is not sufficient only on the interface, because it cannot intercept illegal requests (cross-permission). Therefore, after each request is sent to the back-end, the back-end needs to judge whether the current user has the permission to execute the service. If not, the service will not be executed.

Second, database design

Classic RBAC database.

- department of table
CREATE TABLE DEPT(
    ID INT PRIMARY KEY AUTO_INCREMENT,
    NAME VARCHAR(50));- the employee table
create table `user`(
    id int primary key auto_increment,- user id
    username varchar(50),- login name
    password varchar(50),- the password
    phone varchar(11),-- Mobile phone number
    sex int.- gender
    age int.Age -
    did int
);
- Role table Records information about roles in the system
create table role(
    id int primary key auto_increment,- the character id
    name varchar(255)-- Role name
);
Menu table records all menu information in the system down to button level
create table Menu(
    id int primary key auto_increment,- authorization ID
    name varchar(255),-- Permission name
    resource varchar(255),-- Addresses of resources in the system accessed by the current permission
    pid int.-- Records the parent permission number of the permission
    level int-- Record permission levels (1: level-1 menu 2: level-2 menu, 3: button)
);
- User role table Records the role information of users in the system
create table user_role(
    id int primary key auto_increment,- the primary key
    uid int.- user id
    rid int- the character id
);
-- Department permission table
create table dept_permission(
    id int primary key auto_increment,- the primary key
    did int.-- Department No.
    mid int-- Menu No.
);
-- User permission table
create table user_permission(
    id int primary key auto_increment,- the primary key
    uid int.-- User id
    mid int-- Menu No.
);
- Role permission table
create table role_permission(
    id int primary key auto_increment,- the primary key
    rid int.-- Role Number
    mid int-- Menu No.
);
-- Basic data entry
-- Input department information
INSERT INTO DEPT VALUES(NULL.'Teaching Department'); -- Department No. 1 of Teaching Department
INSERT INTO DEPT VALUES(NULL.'Finance Department'); -- Finance Department No. 2
-- Input user information
INSERT INTO `USER` VALUES(NULL.'qiang'.'123456'.'13666666666'.1.18.1); -- Qiang number 1
INSERT INTO `USER` VALUES(NULL.'cong'.'123456'.'13888888888'.1.18.2);Cong 2
-- Input menu information
INSERT INTO MENU VALUES(NULL.'Teaching management'.' '.0.1);Menu No. 1 None Parent menu Level-1 menu
INSERT INTO MENU VALUES(NULL.'Course Management'.' '.1.2);Menu No. 2 Parent menu No. 1 Secondary menu
INSERT INTO MENU VALUES(NULL.'New Course'.' '.2.3);Menu 3 Parent menu 2 Button
INSERT INTO MENU VALUES(NULL.'Delete course'.' '.2.3);Menu 4 Parent menu 2 Button
INSERT INTO MENU VALUES(NULL.'Modify course'.' '.2.3);Menu 5 Parent menu 2 Button
INSERT INTO MENU VALUES(NULL.'Financial Management'.' '.0.1);Menu No. 6 None Parent menu Level-1 menu
INSERT INTO MENU VALUES(NULL.'Expense Management'.' '.6.2);Menu Number 7 Parent menu 6 Secondary menu
INSERT INTO MENU VALUES(NULL.'Audit reimbursement'.' '.7.3);Menu 8 Parent menu 7 button
INSERT INTO MENU VALUES(NULL.'Apply for reimbursement'.' '.6.2);Menu Number 9 Parent menu 6 Secondary menu
INSERT INTO MENU VALUES(NULL.'withdraw'.' '.9.3);Menu number 10 Parent menu 9 button
INSERT INTO MENU VALUES(NULL.'System Administration'.' '.0.1);Menu No. 11 None Parent menu Level-1 menu
INSERT INTO MENU VALUES(NULL.'Department Rights Management'.' '.11.2);Menu number 12 Parent menu 11 Secondary menu
INSERT INTO MENU VALUES(NULL.'Role Rights Management'.' '.11.2);Menu No. 13 Parent menu 11 Secondary menu
INSERT INTO MENU VALUES(NULL.'User Rights Management'.' '.11.2);Menu Number 14 Parent menu 11 Secondary menu
INSERT INTO MENU VALUES(NULL.'Change Role Permissions'.' '.13.3);Menu 15 Parent menu 13 button
- Enter role information
INSERT INTO ROLE VALUES(NULL.'System Administrator');- Role ID 1
INSERT INTO ROLE VALUES(NULL.'General Manager');- Role number 2
INSERT INTO ROLE VALUES(NULL.'Department Manager');- Role number 3
- Enter user role information
INSERT INTO USER_ROLE VALUES(NULL.1.1);- Qiang of user 1 plays the administrator role
INSERT INTO USER_ROLE VALUES(NULL.2.2);
INSERT INTO USER_ROLE VALUES(NULL.2.3);Cong, user number 2, is general Manager and Department Manager
-- Input department permissions
INSERT INTO DEPT_PERMISSION VALUES(NULL.1.1);-- The teaching department has the teaching management menu
INSERT INTO DEPT_PERMISSION VALUES(NULL.1.2);-- Teaching department has course management under teaching Management menu (query)
INSERT INTO DEPT_PERMISSION VALUES(NULL.1.6);-- The teaching department has a financial management menu
INSERT INTO DEPT_PERMISSION VALUES(NULL.1.9);-- The teaching department owns the financial management menu under the application for reimbursement
INSERT INTO DEPT_PERMISSION VALUES(NULL.2.6);-- The Finance department has the financial management menu
INSERT INTO DEPT_PERMISSION VALUES(NULL.2.7);-- The financial department has the reimbursement management under the menu of financial management
INSERT INTO DEPT_PERMISSION VALUES(NULL.2.8);-- The financial department has the reimbursement management under the menu of financial management (auditing reimbursement)
INSERT INTO DEPT_PERMISSION VALUES(NULL.2.9);-- The Financial department has the application for reimbursement under the financial Management menu
- Enter role permissions
INSERT INTO ROLE_PERMISSION VALUES(NULL.1.1);
INSERT INTO ROLE_PERMISSION VALUES(NULL.1.2);
INSERT INTO ROLE_PERMISSION VALUES(NULL.1.3);
INSERT INTO ROLE_PERMISSION VALUES(NULL.1.4);
INSERT INTO ROLE_PERMISSION VALUES(NULL.1.5);
INSERT INTO ROLE_PERMISSION VALUES(NULL.1.6);
INSERT INTO ROLE_PERMISSION VALUES(NULL.1.7);
INSERT INTO ROLE_PERMISSION VALUES(NULL.1.8);
INSERT INTO ROLE_PERMISSION VALUES(NULL.1.9);
INSERT INTO ROLE_PERMISSION VALUES(NULL.1.10);
INSERT INTO ROLE_PERMISSION VALUES(NULL.1.11);
INSERT INTO ROLE_PERMISSION VALUES(NULL.1.12);
INSERT INTO ROLE_PERMISSION VALUES(NULL.1.13);
INSERT INTO ROLE_PERMISSION VALUES(NULL.1.14);
INSERT INTO ROLE_PERMISSION VALUES(NULL.1.15);
INSERT INTO ROLE_PERMISSION VALUES(NULL.2.1);
INSERT INTO ROLE_PERMISSION VALUES(NULL.2.2);
INSERT INTO ROLE_PERMISSION VALUES(NULL.2.3);
INSERT INTO ROLE_PERMISSION VALUES(NULL.2.4);
INSERT INTO ROLE_PERMISSION VALUES(NULL.2.5);
INSERT INTO ROLE_PERMISSION VALUES(NULL.2.6);
INSERT INTO ROLE_PERMISSION VALUES(NULL.2.7);
INSERT INTO ROLE_PERMISSION VALUES(NULL.2.8);
INSERT INTO ROLE_PERMISSION VALUES(NULL.2.9);
INSERT INTO ROLE_PERMISSION VALUES(NULL.2.10);
SQL > select * from user 1; select * from user 1; select * from user 1;
SELECT M.* FROM USER_ROLE UR LEFT JOIN ROLE_PERMISSION RP ON UR.RID=RP.RID LEFT JOIN MENU M ON RP.MID=M.ID WHERE UR.UID=1 AND M.LEVEL<3
SQL > select * from user 1 where user 1 has permissions (level 1 and level 2)
SELECT M.* FROM USER U LEFT JOIN DEPT_PERMISSION DP ON U.DID=DP.DID LEFT JOIN MENU M ON DP.MID=M.ID WHERE U.ID=1 AND M.LEVEL<3
SQL > select * from user where user = 1;
SELECT M.* FROM USER_PERMISSION UP LEFT JOIN MENU M ON UP.MID=M.ID WHERE UP.UID=1 AND  M.LEVEL<3
-- Merge and de-weight
SELECT M.* FROM USER_ROLE UR LEFT JOIN ROLE_PERMISSION RP ON UR.RID=RP.RID LEFT JOIN MENU M ON RP.MID=M.ID WHERE UR.UID=2 AND M.LEVEL<3
UNION
SELECT M.* FROM USER U LEFT JOIN DEPT_PERMISSION DP ON U.DID=DP.DID LEFT JOIN MENU M ON DP.MID=M.ID WHERE U.ID=2 AND M.LEVEL<3
UNION
SELECT M.* FROM USER_PERMISSION UP LEFT JOIN MENU M ON UP.MID=M.ID WHERE UP.UID=2 AND  M.LEVEL<3
SQL needs to be improved in order to facilitate mybatis to implement one-to-many menu encapsulation
SELECT
        M1.ID ID1,M1.NAME NAME1, M1.RESOURCE RESOURCE1,M1.PID PID1,M1.LEVEL LEVEL1,
        M2.ID ID2,M2.NAME NAME2, M2.RESOURCE RESOURCE2,M2.PID PID2,M2.LEVEL LEVEL2
FROM
        (
          SELECT M.* FROM USER_ROLE UR LEFT JOIN ROLE_PERMISSION RP ON UR.RID=RP.RID LEFT JOIN MENU M ON RP.MID=M.ID WHERE UR.UID=#{UID} AND M.LEVEL=2
          UNION
          SELECT M.* FROM USER U LEFT JOIN DEPT_PERMISSION DP ON U.DID=DP.DID LEFT JOIN MENU M ON DP.MID=M.ID WHERE U.ID=#{UID} AND M.LEVEL=2
          UNION
          SELECT M.* FROM USER_PERMISSION UP LEFT JOIN MENU M ON UP.MID=M.ID WHERE UP.UID=#{UID} AND  M.LEVEL=2
        ) M2 LEFT JOIN MENU M1 ON M2.PID=M1.ID
Copy the code

3. Use Shiro to realize identity authentication

Apache Shiro is ASF’s open source software (Shiro is pronounced “shee-roh”, Japanese for “Castle”) that provides a powerful and flexible security framework. Apache Shiro provides authentication, authorization, encryption, and session management capabilities that hide complex issues and provide a clear and intuitive API that makes it easy for developers to develop their own application security code. Subject: that is, “user”. All external applications interact with Subject, who records the current operating user and understands the concept of user as the Subject of the current operation, which may be a user requesting through a browser or a running program. Subject is an interface in Shiro, which defines many authentication and authorization-related methods. External programs authenticate and authorize through Subject. Subject authenticates and authorizes through the SecurityManager SecurityManager (subject is the facade of SecurityManager). SecurityManager: SecurityManager, which is the core of shiro and is responsible for security management of all subjects. You can use the SecurityManager to authenticate and authorize a Subject. Authentication: Is a component that authenticates the user (login). Authorization: Indicates that users are authenticated by the Authorization. When accessing functions, the Authorization is required to determine whether users have the operation permission for the functions. It’s just to determine whether you have permission, authorization, and essentially access control, which urls you can access. Realm: A Realm that encapsulates authentication and authorization operations. If user identity data is in the database, then a Realm needs to retrieve user identity information from the database. Before using Shiro must first clear Shiro work content, Shiro only be responsible for authentication and authentication, the user is not responsible for managing the permissions, that is to say whether the page button displays, what role in the system, users have what roles, each role what are the corresponding permissions, we need to yourself to achieve all these, In other words, Shiro can only work with existing data, not modify the data in the database. 1. Introduce Shiro dependencies

<! --shiro -->
<dependency>
  <groupId>org.apache.shiro</groupId>
  <artifactId>shiro-spring</artifactId>
  <version>1.4.0</version>
</dependency>
Copy the code

2. Create a domain class Create a domain class that encapsulates login and authorization operations.

/* Encapsulates authentication and authorization operations */
public class UserRealm extends AuthorizingRealm {
    // Encapsulate the login method
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        return null;
    }
    // Encapsulate authorization methods
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        return null; }}Copy the code

3. Initialize Shiro configuration domain, configure security manager, configure filter request -> filter -> Determine whether it is necessary to log in according to the blacklist -> Blacklist -> determine the subject corresponding to the session, determine whether the subject has logged in, if not logged in redirect to a page.

@Configuration
public class ShiroConfig {
    @Bean
    public UserRealm initUserRealm(a){
        return new UserRealm();
    }
    @Bean
    public SecurityManager initSecurityManager(a){
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(initUserRealm());
        return securityManager;
    }
    @Bean
    public ShiroFilterFactoryBean shiroFilter(a) throws UnsupportedEncodingException {
        // Instantiate Shiro filter factory
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        // Inject the security manager into the factory
        shiroFilterFactoryBean.setSecurityManager(initSecurityManager());
        // Create an ordered key-value pair to store the blacklist and whitelist
        Map<String,String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
        //anon represents the address of a resource that can be accessed without logging in
        filterChainDefinitionMap.put("/page/login.html"."anon");
        filterChainDefinitionMap.put("/user"."anon");
        // Resources that need to be accessed after login
        filterChainDefinitionMap.put("/ * *"."authc");
        // If you have not logged in to shiro's automatic redirection address
        shiroFilterFactoryBean.setLoginUrl("/page/login.html");
        // Configure the whitelist to shiro filter
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        returnshiroFilterFactoryBean; }}Copy the code

4. Complete the login using Shiro• Encapsulate the user name and password as UsernamePasswordToken in the control layer

UsernamePasswordToken token=new UsernamePasswordToken(username,password);
Copy the code

• Retrieve Subject object from SecurityUtils Subject = securityutils.getSubject (); Subject is the principal object, Shiro’s abstraction of the user. When a user accesses the server for the first time, the request passes through Shiro’s filter, where an HttpSession object is created. When you create a Subject object, the logon status of the Subject object is unlogged (unauthenticated). Store the Subject in the SecurityManager, which is the current user principal object as long as HttpSession does not change. The Subject object automatically stores User information after successful login, and obtains User information through the Subject object for subsequent use.

 User user = (User) SecurityUtils.getSubject().getPrincipal();
Copy the code

• Determine login status. If not, log in via Subject

 if(! subject.isAuthenticated()){ subject.login(token); }Copy the code

• The login method is executed, and the login service is invoked in the authentication method of the domain class. The authentication information object is encapsulated according to the returned value

@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
    String username= (String) authenticationToken.getPrincipal();// Retrieve the user name
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    wrapper.eq("username",username);
    User user = userService.getOne(wrapper);
    // Encapsulate as an authentication message object
    SimpleAuthenticationInfo info=null;
    if(user! =null){
        info=new SimpleAuthenticationInfo(user, user.getPassword(), getName());
    }
    return info;
}
Copy the code

When the user does not exist, the return value is NULL. Once NULL is returned, Shiro assumes that the user name does not exist and throws an exception indicating that the account name does not exist. If the user exists, the authentication information of the user needs to be returned to Shiro. Shiro will check whether the queried password is the same as the password in the token. If the password is inconsistent, Shiro will throw an exception indicating that the password is incorrect. Therefore, we need to provide global exception handlers to handle these two types of exceptions and make corresponding responses respectively.

@ControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(UnknownAccountException.class)
    @ResponseBody
    public JSONResult handlerUnknowAccountException(a){
        return new JSONResult("1002"."User name does not exist".null.null);
    }
    @ResponseBody
    @ExceptionHandler(IncorrectCredentialsException.class)
    public JSONResult handlerIncorrectCredentialsException(a){
        return new JSONResult("1002"."Password error".null.null); }}Copy the code

RememberMe

1. Configure the Cookie manager in ShiroConfig. The purpose of configuring the Cookie manager is to set the Cookie name and AES encryption secret key, and encrypt a 24-length string into a 16-length byte array through Base64. The AES encryption key must contain 128 bits, 192 bits, or 256 bits.

@Bean
public CookieRememberMeManager initCookieRememberMeManager(a){
    CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
    SimpleCookie rememberMe = new SimpleCookie("rememberMe");
    rememberMe.setMaxAge(7*24*60*60);
    cookieRememberMeManager.setCookie(rememberMe);
    // Set the encryption key
    cookieRememberMeManager.setCipherKey(Base64.decode("Woniuxywuyanzu520niubi=="));
    return cookieRememberMeManager;
}
// Add the Cookie manager to the security manager
@Bean
public SecurityManager initSecurityManager(a){
    DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
    securityManager.setRealm(initUserRealm());
    securityManager.setRememberMeManager(initCookieRememberMeManager());
    return securityManager;
}
Copy the code

2. Modify the login page to provide the remember me option

<input type="checkbox" v-model="user.remember">No login for 7 days<script>
new Vue({
    data: {user: {username:"".password:"".remember:true}}});</script>
Copy the code

3, in the control layer receive remember my parameters in the control layer receive front-end transfer boxes, remember parameters, directly to the data encapsulation to the Token, if the value is true, Shiro will unlock the function of RememberMe, if is false is not open. After the function is enabled, the Subject data will be serialized and encrypted into the Cookie after successful login. Note that since the User data is stored in Subject, the User data is also serialized. The User class must implement the serialization interface.

//1. Encapsulate the user name and password as tokens
UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(),user.getPassword(),remember);
Copy the code

//2. Invoke the authentication method provided by Subject

Subject subject = SecurityUtils.getSubject();
Copy the code

//3. Check the login status of the current user

if(! subject.isAuthenticated()&&! subject.isRemembered()){ subject.login(token); }Copy the code

4, change the blocking status of /** to user, indicating that these resources can be accessed normally in the authentication state and remember me state. // User indicates authentication or remember I can access both

filterChainDefinitionMap.put("/ * *"."user");
Copy the code

Add a deregister address mapping in the filter configuration of ShiroConfig: // Add deregister address

filterChainDefinitionMap.put("/logout"."logout");
Copy the code

// User indicates authentication or remember I can access both

filterChainDefinitionMap.put("/ * *"."user");
Copy the code

You complete the logout by accessing the /logout address via a hyperlink on a web page. All requests are filtered by Shiro, and if Shiro sees that we are accessing the logout address, it clears the cookie, changes the Subject state, and redirects to the login login page. Vi. Back-end Permission Verification Procedure:

Configuration process: 1. Improve the authorization method query database in the domain class to query all permissions of the user, encapsulate these permissions information into SimpleAuthorizationInfo object, and use the menu name as the permission name.

// Encapsulate authorization methods
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
    // Query all user permissions
    User user = (User) principalCollection.getPrimaryPrincipal();
    SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
    try {
        List<Menu> permissions = menuService.selectPermission(user.getId());
        for(Menu menu:permissions){ simpleAuthorizationInfo.addStringPermission(menu.getName()); }}catch (Exception e) {
        e.printStackTrace();
    }
    return simpleAuthorizationInfo;
}
Copy the code

Add two beans to ShiroConfig: a notification class and a proxy class.

 // Permissions verify the AOP configuration
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(a) {
    AuthorizationAttributeSourceAdvisor advisor=new AuthorizationAttributeSourceAdvisor();
    advisor.setSecurityManager(initSecurityManager());
    return advisor;
}
@Bean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator(a){
    DefaultAdvisorAutoProxyCreator app=new DefaultAdvisorAutoProxyCreator();
    app.setProxyTargetClass(true);
    return app;
}
Copy the code

For RequiresPermissions({permissions 1, permissions 2}), write a comment on the controller method to describe the permissions required for that method.

 @GetMapping
RequiresPermissions({" Character Management "})
public JSONResult select(a) throws Exception{
    return new JSONResult("1000"."success".null,roleService.list());
}
4In the global exception handler for no permission exceptions@ExceptionHandler(AuthorizationException.class)
public JSONResult handlerAuthorizationException(a){
    return new JSONResult("1004"."Insufficient authority".null.null);
}
Copy the code

No matter you have any problems in your study, Chongqing Snail College welcomes you to come to consult, contact QQ: 296799112