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