Integrated use of Shiro permission management in SpringBoot

Keywords: Shiro Apache Java JSP

When integrating Shiro, we first need to determine our steps:

1. Add Shiro's dependency package to implement its own Realm class (by inheriting the AuthorizingRealm class);

2. Implement Shiro's configuration class

3. Implement the front-end login interface and Controller class

 

Step one:

Add dependency package to pom.xml

<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>1.4.0</version>
 </dependency>

Implement the Realm class


package ariky.shiro.realm;
 
import java.util.HashSet;
import java.util.Set;
 
import javax.servlet.http.HttpServletRequest;
 
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.apache.shiro.web.subject.WebSubject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
 
/**
* @ClassName:
* @Description: Realm Configuration
* @author fuweilian
* @date 2018-5-12 11:36:41 am
 */
public class MyShiroRealm extends AuthorizingRealm {
    //slf4j records logs, which can not be used
    private Logger logger = LoggerFactory.getLogger(MyShiroRealm.class);
 
    /**
     * Set authorization information
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        logger.info("Start authorization(doGetAuthorizationInfo)");
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        HttpServletRequest request = (HttpServletRequest) ((WebSubject) SecurityUtils
                .getSubject()).getServletRequest();//This can be used to obtain other additional parameter information submitted during login
        String username = (String) principals.getPrimaryPrincipal();//This is the demo written here. Later, in the actual project, traditional Chinese medicine obtains the user's role and authority through the login account. This is written dead
        //Acceptance authority
        //role
        Set<String> roles = new HashSet<String>();
        roles.add("role1");
        authorizationInfo.setRoles(roles);
        //Jurisdiction
        Set<String> permissions = new HashSet<String>();
        permissions.add("user:list");
        //permissions.add("user:add");
        authorizationInfo.setStringPermissions(permissions);
        return authorizationInfo;
    }
 
    /**
     * Set authentication information
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(
            AuthenticationToken authenticationToken) throws AuthenticationException {
        logger.info("Start certification(doGetAuthenticationInfo)");
        //UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
        HttpServletRequest request = (HttpServletRequest) ((WebSubject) SecurityUtils
                .getSubject()).getServletRequest();
        UsernamePasswordToken token = new UsernamePasswordToken (request.getParameter("userName"),request.getParameter("password"));
        //Get the account entered by the user
        String userName = (String)token.getPrincipal();
        //Go to the database to match the user information through userName, and do the following processing by querying the user's situation
        //Write it dead for the time being, and deal with it according to the login user account
        logger.info("Account number:"+userName);
        if("passwordError".equals(userName)){//Password error
            throw new IncorrectCredentialsException(); 
        }else if("lockAccount".equals(userName)){// User locking
            throw new LockedAccountException(); 
        }else{
            SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
                    userName, //User name
                    "123456", //Password, write dead
                    ByteSource.Util.bytes(userName+"salt"),//salt=username+salt
                    getName()  //realm name
            );
            return authenticationInfo;
        }
    }
    
}

The second step is to implement Shiro's configuration class:


package ariky.shiro.configuration;
 
import java.util.LinkedHashMap;
import java.util.Map;
 
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
 
import ariky.shiro.realm.MyShiroRealm;
 
/**
* @ClassName: ShiroConfiguration 
* @Description: shiro Configuration class 
* @author fuweilian
* @date 2018-5-12 11:05:09 am
 */
@Configuration
public class ShiroConfiguration {
    private static Logger logger = LoggerFactory.getLogger(ShiroConfiguration.class);
    @Bean(name = "shiroFilter")
    public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager){
        logger.info("Get into shiroFilter......");
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        //Set a path that does not need to be blocked
        Map<String,String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
        //Judge in order
        filterChainDefinitionMap.put("/static/**", "anon");
        //Configure the exit filter, the specific exit code Shiro has implemented for us
        filterChainDefinitionMap.put("/logout", "logout");
        //<!-- authc:All URLs must be authenticated to be accessible; anon: all URLs can be accessed anonymously -- >
        /************************************Initialize all permission information start******************************************/
        //Here, if you want to use it in a project later, you can query it directly from the database
        filterChainDefinitionMap.put("/user/list", "authc,perms[user:list]");
        //filterChainDefinitionMap.put("/user/add", "authc,perms[user:add]");
        /***************************************Initialization of all permission information begins and ends*********************************************/
        filterChainDefinitionMap.put("/**", "authc");
        // If it is not set, it will automatically find the "/ login.jsp" page in the root directory of the Web project
        shiroFilterFactoryBean.setLoginUrl("/login");
        // Link to jump after successful login
        shiroFilterFactoryBean.setSuccessUrl("/index");
        //Unauthorized interface
        shiroFilterFactoryBean.setUnauthorizedUrl("/error/403");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }
    
    @Bean
    public MyShiroRealm myShiroRealm(){
         MyShiroRealm myShiroRealm = new MyShiroRealm();
         //You can set the caching mechanism here later
         return myShiroRealm;
    } 
    
    @Bean
    public SecurityManager securityManager(){
        DefaultWebSecurityManager securityManager =  new DefaultWebSecurityManager();
        securityManager.setRealm(myShiroRealm());
        return securityManager;
    }
    
    
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }
    
}

Step 3: implement the destroyer class. There are two classes written here. One is the LoginController processing class of login information, and the other is the UserController for testing permissions

1.LoginController.java


package ariky.controller;
 
import java.util.Map;
 
import javax.servlet.http.HttpServletRequest;
 
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.UnknownAccountException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
 
/**
* @ClassName: LoginController 
* @Description: controller of login control
* @author fuweilian
* @date 2018-5-12 01:15:46 PM
 */
@RequestMapping
@Controller
public class LoginController {
    private Logger logger = LoggerFactory.getLogger(LoginController.class);
    
    @RequestMapping(value="/login",method=RequestMethod.GET)
    public String getLogin(){
        logger.info("Get into login page");
        return "login";
    }
    
    @RequestMapping(value="/login",method=RequestMethod.POST)
    public String doLogin(HttpServletRequest req,Map<String, Object> model){
        logger.info("Enter login processing");
        String exceptionClassName = (String) req.getAttribute("shiroLoginFailure");
        logger.info("exceptionClassName:"+exceptionClassName);
        String error = null;
        if (UnknownAccountException.class.getName().equals(exceptionClassName)) {
            error = "User name/Password error";
        } else if (IncorrectCredentialsException.class.getName().equals(exceptionClassName)) {
            error = "User name/Password error";
        }else if(LockedAccountException.class.getName().equals(exceptionClassName)){ 
            error = "User locked or deleted";
        }else if (exceptionClassName != null) {
            error = "Other errors:" + exceptionClassName;
        }
        if(SecurityUtils.getSubject().isAuthenticated()){//There is no error, but you have already signed in, and you can directly jump to the welcom page
            model.put("name", req.getParameter("userName"));
            return "index";
        }else{//Erroneous
            model.put("error", error);
            return "login";
        }
    }
    @RequestMapping("/index")
    public String index(){
        return "index";
    }
}

2.UserController.java


package ariky.controller;
 
import java.util.ArrayList;
import java.util.List;
 
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
 
/**
* @ClassName: UserController 
* @Description: User processing Controller
* @author fuweilian
* @date 2018-5-12 03:11:06 PM
 */
@Controller
@RequestMapping("/user")
public class UserController {
    Logger logger = LoggerFactory.getLogger(UserController.class);
    @RequiresPermissions("user:list")//This is to configure whether this permission is available. If it is written according to the above method, this permission is available
    @RequestMapping(value="/list",method=RequestMethod.GET)
    public String getList(){
        logger.info("Enter user list");
        return "user/list";
    }
    @RequiresPermissions(value={"user:add"})//This is not authorized
    @RequestMapping(value="/add",method=RequestMethod.GET)
    public String getAdd(){
        logger.info("Enter the new user interface");
        return "user/add";
    }
    
}

Front end interface: there are five interfaces (login.jsp, index.jsp, list.jsp, add.jsp, 403.jsp)

The directory structure is:

 

1.login.jsp

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Login</title>
</head>
 
<body>
    <h1>Login page----${error}</h1>
    <form:form action="${pageContext.request.contextPath }/login"
         method="post">
        User name:<input type="text" name="userName">
        <br />
        Password:<input type="passwordParam" name="password"/>
        <input type="submit" value="Submission"/>
    </form:form>
</body>
</html>

2.index.jsp

<%@ page language="java" pageEncoding="UTF-8"%>
 <%@taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>First example</title>
<script src="${pageContext.request.contextPath }/webjars/jquery/2.1.4/jquery.js"></script>
<script src="${pageContext.request.contextPath }/webjarslocator/jquery/jquery.js"></script>
</head>
 
<body>
    <h1>${name}:Hello, welcome to this website</h1>
        <shiro:hasPermission name="user:list"><!-- this a The label is visible -->
            <a href="${pageContext.request.contextPath }/user/list" target="_blank">Jump to user list(Have jurisdiction)</a>
        </shiro:hasPermission>
    <br/>
        <shiro:hasPermission name="user:add"><!-- this a The label is invisible -->
            <a href="${pageContext.request.contextPath }/user/add" target="_blank">Jump to new user list(No authority)</a>
        </shiro:hasPermission>
</body>
</html>

3.list.jsp is almost the same as add.jsp and 403.jsp. Here's one. It's just for demo. In actual projects, the actual project should prevail

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>userList</title>
</head>
 
<body>
    <h1>User list information</h1>
</body>
</html>

If the startup is successful, you can enter the login login interface to test the permission authentication of shiro. The above codes are all write dead. If you want to realize dynamic permission management and user permission management, you need to do some other processing. The user's dynamic permission only needs to query the database when authorized in his ShiroRealm class, and dynamic authorization and role are OK. For dynamic permission management, the following methods can be implemented. After modifying the permission data, just update the configuration in shiro. See the following code specifically. This is demo, not the actual project. In the actual project, it's better not to write the logic in the Controller


package ariky.shiro.controller;
 
import java.util.LinkedHashMap;
import java.util.Map;
 
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.filter.mgt.DefaultFilterChainManager;
import org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver;
import org.apache.shiro.web.servlet.AbstractShiroFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
 
/**
* @ClassName: PermssionController 
* @Description: controller of permission Operation 
* @author fuweilian
* @date 2018-5-12 04:59:15 PM
 */
@Controller
@RequestMapping("permssion")
public class PermssionController {
    
     @Autowired
     ShiroFilterFactoryBean shiroFilterFactoryBean;
     
     /**
     * @Title: updatePermssion 
     * @author: fuweilian
     * @Description: This is written in the controller for the time being. It is not written according to the rules. It will be written when it is used in the project
     * @return  Parameter description 
     * @return Object    Return type 
     * @throws
      */
    @RequestMapping("/updatePermssion")
    @ResponseBody
    public Object updatePermssion(){
        synchronized (shiroFilterFactoryBean){
            AbstractShiroFilter shiroFilter = null;
            try {
                shiroFilter = (AbstractShiroFilter) shiroFilterFactoryBean
                        .getObject();
                PathMatchingFilterChainResolver filterChainResolver = (PathMatchingFilterChainResolver) shiroFilter
                    .getFilterChainResolver();
                DefaultFilterChainManager manager = (DefaultFilterChainManager) filterChainResolver
                    .getFilterChainManager();
                // Clear old permission control
                manager.getFilterChains().clear();
                shiroFilterFactoryBean.getFilterChainDefinitionMap().clear();
                //The latter one can be obtained directly from the database
                Map<String,String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
                //Judge in order
                filterChainDefinitionMap.put("/static/**", "anon");
                //Configure the exit filter, the specific exit code Shiro has implemented for us
                filterChainDefinitionMap.put("/logout", "logout");
                //<!-- authc:All URLs must be authenticated to be accessible; anon: all URLs can be accessed anonymously -- >
                /************************************Initialize all permission information start******************************************/
                //Here, if you want to use it in a project later, you can query it directly from the database
                filterChainDefinitionMap.put("/user/list", "authc,perms[user:list]");
                filterChainDefinitionMap.put("/user/add", "authc,perms[user:add]");
                /***************************************Initialization of all permission information begins and ends*********************************************/
                filterChainDefinitionMap.put("/**", "authc");
                //
                shiroFilterFactoryBean.setLoginUrl("/login");
                // Link to jump after successful login
                shiroFilterFactoryBean.setSuccessUrl("/index");
                //Unauthorized interface
                shiroFilterFactoryBean.setUnauthorizedUrl("/error/403");
                shiroFilterFactoryBean
                    .setFilterChainDefinitionMap(filterChainDefinitionMap);
                // Rebuild build
                Map<String, String> chains = shiroFilterFactoryBean
                        .getFilterChainDefinitionMap();
                for (Map.Entry<String, String> entry : chains.entrySet()) {
                    String url = entry.getKey();
                    String chainDefinition = entry.getValue().trim()
                            .replace(" ", "");
                    manager.createChain(url, chainDefinition);
                }
                return "Update permission succeeded";  
            } catch (Exception e) {
                throw new RuntimeException(
                        "To update shiro Permission error!");
            }
        }
    }
 
}

/*
Navicat MySQL Data Transfer
Source Server         : arikyDB
Source Server Version : 50721
Source Host           : 47.106.95.168:3306
Source Database       : ariky
Target Server Type    : MYSQL
Target Server Version : 50721
File Encoding         : 65001
Date: 2018-05-14 16:05:51
*/
 
SET FOREIGN_KEY_CHECKS=0;
 
-- ----------------------------
-- Table structure for common_permssion
-- ----------------------------
DROP TABLE IF EXISTS `common_permssion`;
CREATE TABLE `common_permssion` (
  `ID` int(11) NOT NULL AUTO_INCREMENT COMMENT 'Primary key ID',
  `NAME` varchar(255) DEFAULT NULL COMMENT 'Permission name',
  `TYPE` varchar(255) DEFAULT NULL COMMENT 'Type button(button)Or menu(menu) ',
  `PARENT_ID` int(11) DEFAULT NULL COMMENT 'Superior ID',
  `PARENT_IDS` varchar(255) DEFAULT NULL COMMENT 'Superior PIDs',
  `URL` varchar(255) DEFAULT NULL COMMENT 'Access path',
  `ICONCLS` varchar(255) DEFAULT NULL COMMENT 'Icon(Can not)',
  `PERMISSION` varchar(255) DEFAULT NULL COMMENT 'Jurisdiction(as user:list)',
  `ORDER_NUM` int(11) DEFAULT NULL COMMENT 'sort',
  `REMARK` varchar(255) DEFAULT NULL COMMENT 'Remarks',
  PRIMARY KEY (`ID`)
) ENGINE=InnoDB AUTO_INCREMENT=41 DEFAULT CHARSET=utf8 COMMENT='This table is used to store resource permission information';
 
-- ----------------------------
-- Table structure for common_role
-- ----------------------------
DROP TABLE IF EXISTS `common_role`;
CREATE TABLE `common_role` (
  `ID` int(11) NOT NULL AUTO_INCREMENT COMMENT 'Primary key ID',
  `LABEL_ID` varchar(255) DEFAULT NULL COMMENT 'Label Id',
  `NAME` varchar(255) DEFAULT NULL COMMENT 'Role name',
  `ROLE` varchar(255) DEFAULT NULL,
  `DESCRIPTION` varchar(255) DEFAULT NULL,
  `IS_SHOW` int(11) DEFAULT '1' COMMENT 'Determine whether the role is in use (1: use, 2: disable)',
  `IS_HANDLER` int(2) DEFAULT NULL COMMENT 'Determine the role (1: background role, 2: merchant administrator role, 3: merchant adds user role, 4: Tourist role)',
  PRIMARY KEY (`ID`)
) ENGINE=InnoDB AUTO_INCREMENT=15 DEFAULT CHARSET=utf8 COMMENT='Role table';
 
-- ----------------------------
-- Table structure for common_role_permssion
-- ----------------------------
DROP TABLE IF EXISTS `common_role_permssion`;
CREATE TABLE `common_role_permssion` (
  `ID` int(11) NOT NULL AUTO_INCREMENT COMMENT 'Primary key Id',
  `ROLE_ID` int(11) DEFAULT NULL COMMENT 'role Id',
  `RESOURCE_ID` int(11) DEFAULT NULL COMMENT 'Resources (permissions) Id',
  PRIMARY KEY (`ID`)
) ENGINE=InnoDB AUTO_INCREMENT=493 DEFAULT CHARSET=utf8 COMMENT='Role resource permission table middle table';

 

752 original articles published, praised 649, visited 1.28 million+
His message board follow

Posted by vivianp79 on Mon, 02 Mar 2020 18:37:12 -0800