This article is followed by the last blog: Introduction to Spring boot (3): Spring boot integration combined with AdminLTE(Freemarker), using generation to automatically generate code, using DataTable and Page Helper for paging display . According to the previous blog, we can build a simple Spring Boot system. This blog continues to transform this system. It mainly integrates Shiro authorization framework, Shiro section, and my previous blog.( Authentication and Shiro Security Framework ) There are introductions. There are no redundant introductions here.
This series of blogs is part of practice, focusing on the process of code and system building. If you encounter professional terms, you can find their meaning by yourself.
1.Shiro configuration class
So far, the system has been mainly used in three configuration classes, all related to Shiro. Later, with the expansion of the project, the number of configuration files will increase.
- FreeMarker Config: For the configuration of FreeMarker page display, regarding Shiro section, a shared variable is set for Shiro tag. If this variable is not set, the FreeMarker page will not recognize Shiro tag. Its main code is as follows:
1 configuration.setSharedVariable("shiro", new ShiroTags());
- MShiroFilterFactoryBean: Setting up filters, of course, can also configure filters in Config files. The disadvantage is that session read and update access time are done in each request, which increases the processing load in the case of cluster deployment session sharing. Distributed will be used later in this project, so the filter and Config configuration file will be separated directly to improve efficiency.
1 private final class MSpringShiroFilter extends AbstractShiroFilter { 2 protected MSpringShiroFilter(WebSecurityManager webSecurityManager, FilterChainResolver resolver) { 3 super(); 4 if (webSecurityManager == null) { 5 throw new IllegalArgumentException("WebSecurityManager property cannot be null."); 6 } 7 setSecurityManager(webSecurityManager); 8 if (resolver != null) { 9 setFilterChainResolver(resolver); 10 } 11 } 12 13 @Override 14 protected void doFilterInternal(ServletRequest servletRequest, ServletResponse servletResponse, 15 FilterChain chain) throws ServletException, IOException { 16 HttpServletRequest request = (HttpServletRequest) servletRequest; 17 String str = request.getRequestURI().toLowerCase(); 18 boolean flag = true; 19 int idx = 0; 20 if ((idx = str.indexOf(".")) > 0) { 21 str = str.substring(idx); 22 if (ignoreExt.contains(str.toLowerCase())) 23 flag = false; 24 } 25 if (flag) { 26 super.doFilterInternal(servletRequest, servletResponse, chain); 27 } else { 28 chain.doFilter(servletRequest, servletResponse); 29 } 30 } 31 32 }
- Shiro Configuration: General configuration file, which is Shiro's basic general configuration file. As long as it is integrated with Shiro, it must have this file. It mainly configures information related to Shiro's login authentication. Its code is as follows:
1 /** 2 * Set up shiro's cache, and the cache parameters are all configured in the xml file 3 * @return 4 */ 5 @Bean 6 public EhCacheManager getEhCacheManager() { 7 EhCacheManager em = new EhCacheManager(); 8 em.setCacheManagerConfigFile("classpath:ehcache/ehcache-shiro.xml"); 9 return em; 10 } 11 /** 12 * Credential matcher 13 * (Because our password checking was handed over to Shiro's Simple Authentication Info for processing 14 * So we need to modify the code in doGetAuthentication Info. 15 * ) 16 * @return 17 */ 18 @Bean 19 public HashedCredentialsMatcher hashedCredentialsMatcher(){ 20 HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher(); 21 hashedCredentialsMatcher.setHashAlgorithmName("md5");//hash:Use here MD5 algorithm; 22 hashedCredentialsMatcher.setHashIterations(1);//The number of hashes, such as two, equals md5(md5("")); 23 return hashedCredentialsMatcher; 24 } 25 /** 26 * 27 * Master file 28 */ 29 @Bean(name = "myShiroRealm") 30 public UserRealm myShiroRealm(EhCacheManager cacheManager) { 31 UserRealm realm = new UserRealm(); 32 realm.setCacheManager(cacheManager); 33 realm.setCredentialsMatcher(hashedCredentialsMatcher()); 34 return realm; 35 } 36 //Conversation ID generator 37 @Bean(name = "sessionIdGenerator") 38 public JavaUuidSessionIdGenerator javaUuidSessionIdGenerator(){ 39 JavaUuidSessionIdGenerator javaUuidSessionIdGenerator = new JavaUuidSessionIdGenerator(); 40 return javaUuidSessionIdGenerator; 41 } 42 @Bean(name = "sessionIdCookie") 43 public SimpleCookie getSessionIdCookie(){ 44 SimpleCookie sessionIdCookie = new SimpleCookie("sid"); 45 sessionIdCookie.setHttpOnly(true); 46 sessionIdCookie.setMaxAge(-1); 47 return sessionIdCookie; 48 49 } 50 /*<!-- Conversation DAO - >*/ 51 @Bean(name = "sessionDAO") 52 public EnterpriseCacheSessionDAO enterpriseCacheSessionDAO(){ 53 EnterpriseCacheSessionDAO sessionDao = new EnterpriseCacheSessionDAO(); 54 sessionDao.setSessionIdGenerator(javaUuidSessionIdGenerator()); 55 sessionDao.setActiveSessionsCacheName("shiro-activeSessionCache"); 56 return sessionDao; 57 } 58 @Bean(name = "sessionValidationScheduler") 59 public ExecutorServiceSessionValidationScheduler getExecutorServiceSessionValidationScheduler() { 60 ExecutorServiceSessionValidationScheduler scheduler = new ExecutorServiceSessionValidationScheduler(); 61 scheduler.setInterval(1800000); 62 return scheduler; 63 } 64 @Bean(name = "sessionManager") 65 public DefaultWebSessionManager sessionManager(EnterpriseCacheSessionDAO sessionDAO){ 66 DefaultWebSessionManager sessionManager = new DefaultWebSessionManager(); 67 sessionManager.setGlobalSessionTimeout(1800000); 68 sessionManager.setDeleteInvalidSessions(true); 69 sessionManager.setSessionValidationSchedulerEnabled(true); 70 sessionManager.setSessionValidationScheduler(getExecutorServiceSessionValidationScheduler()); 71 sessionManager.setSessionDAO(sessionDAO); 72 sessionManager.setSessionIdCookieEnabled(true); 73 sessionManager.setSessionIdCookie(getSessionIdCookie()); 74 return sessionManager; 75 } 76 @Bean(name = "lifecycleBeanPostProcessor") 77 public LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() { 78 return new LifecycleBeanPostProcessor(); 79 } 80 @Bean 81 public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() { 82 DefaultAdvisorAutoProxyCreator daap = new DefaultAdvisorAutoProxyCreator(); 83 daap.setProxyTargetClass(true); 84 return daap; 85 } 86 @Bean(name = "securityManager") 87 public DefaultWebSecurityManager getDefaultWebSecurityManager(UserRealm myShiroRealm, DefaultWebSessionManager sessionManager) { 88 DefaultWebSecurityManager dwsm = new DefaultWebSecurityManager(); 89 dwsm.setRealm(myShiroRealm); 90 // <!-- User authorization/Authentication information Cache, Use EhCache cache --> 91 dwsm.setCacheManager(getEhCacheManager()); 92 dwsm.setSessionManager(sessionManager); 93 return dwsm; 94 } 95 /** 96 * Open shiro aop annotation support. 97 * Use proxy mode; so code support needs to be turned on; 98 * @param securityManager 99 * @return 100 */ 101 @Bean 102 public AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) { 103 AuthorizationAttributeSourceAdvisor aasa = new AuthorizationAttributeSourceAdvisor(); 104 aasa.setSecurityManager(securityManager); 105 return aasa; 106 } 107 /** 108 * ShiroFilter<br/> 109 * Note that Student Service and IScoreDao in the parameters are just examples, because we can get the relevant objects accessing the database in this way. 110 * Then read the database configuration and configure it into the access rules of shiroFilterFactoryBean. In a real project, use your own Service to handle business logic. 111 * 112 */ 113 @Bean(name = "shiroFilter") 114 public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultWebSecurityManager securityManager) { 115 ShiroFilterFactoryBean shiroFilterFactoryBean = new MShiroFilterFactoryBean(); 116 // Must set SecurityManager 117 shiroFilterFactoryBean.setSecurityManager(securityManager); 118 // If you do not set the default, it will automatically find Web Project Root Catalogue"/login.jsp"page 119 shiroFilterFactoryBean.setLoginUrl("/login"); 120 // Connections to jump after successful login 121 shiroFilterFactoryBean.setSuccessUrl("/certification"); 122 //shiroFilterFactoryBean.setSuccessUrl("/index"); 123 shiroFilterFactoryBean.setUnauthorizedUrl("/403"); 124 loadShiroFilterChain(shiroFilterFactoryBean); 125 return shiroFilterFactoryBean; 126 } 127 /** 128 * Load shiroFilter privilege control rules (read from database and configure) 129 * 130 */ 131 private void loadShiroFilterChain(ShiroFilterFactoryBean shiroFilterFactoryBean){ 132 /////////////////////// The following rules are best configured in the configuration file /////////////////////// 133 Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>(); 134 // authc: The page under this filter must be validated before it can be accessed. It is Shiro A built-in interceptor org.apache.shiro.web.filter.authc.FormAuthenticationFilter 135 filterChainDefinitionMap.put("/login", "authc"); 136 filterChainDefinitionMap.put("/logout", "logout"); 137 // anon: Its corresponding filter is empty inside.,You've done nothing at all? 138 logger.info("##################Read permission rules from the database and load them to shiroFilter in##################"); 139 // filterChainDefinitionMap.put("/user/edit/**", "authc,perms[user:edit]");// For testing purposes, fixed write-to-death values can also be read from databases or other configurations. 140 shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); 141 }
2. Login Authentication and Authority Management
Mainly rewrite Realm domain, complete authority authentication and authority management:
1 protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { 2 //If no permission validation is done, all you need to do here is return null that will do 3 SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo(); 4 String userName = (String) principals.getPrimaryPrincipal(); 5 Result<TUser> list = userService.getUserByUsername(userName); 6 if(list.isStatus()) { 7 //Get the role that the user belongs to 8 Result<List<TRole>> resultRole = roleService.getRoleByUserId(list.getResultData().getUserId()); 9 if(resultRole.isStatus()) { 10 HashSet<String> role = new HashSet<String>(); 11 for(TRole tRole : resultRole.getResultData()) { 12 role.add(tRole.getRoleId()+""); 13 } 14 //Get the permissions that the role has 15 Result<List<TPermission>> resultPermission = permissionService.getPermissionsByRoleId(role); 16 if(resultPermission.isStatus()) { 17 HashSet<String> permissions = new HashSet<String>(); 18 for(TPermission tPermission : resultPermission.getResultData()) { 19 permissions.add(tPermission.getPermissionsValue()); 20 } 21 System.out.println("Jurisdiction:"+permissions); 22 authorizationInfo.setStringPermissions(permissions); 23 } 24 } 25 } 26 //return null; 27 return authorizationInfo; 28 } 29 30 @Override 31 protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { 32 //Authentication login 33 String username = (String) authenticationToken.getPrincipal(); 34 //String password = new String((char[]) authenticationToken.getCredentials()); 35 Result<TUser> result = userService.getUserByUsername(username); 36 if (result.isStatus()) { 37 TUser user = result.getResultData(); 38 return new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(), getName()); 39 } 40 //return new SimpleAuthenticationInfo(user., "123456", getName()); 41 return null; 42 } 43 }
2.1. Login Authentication
First, create a front-end login interface and make a simple login Form form.

Click on login to send a request in the background. It must be a Post request. Otherwise Shiro cannot recognize it. The authentication part is mainly completed in Ream. A new class is created, inherits AuthorizingRealm, and then rewrites the doGetAuthentication Info method:


1 @RequestMapping(value = "/login", method = RequestMethod.POST) 2 public String postLogin(RedirectAttributes redirectAttributes, HttpServletRequest request, HttpSession session) { 3 // Logon failure from request Get in shiro Processing exception information. 4 // shiroLoginFailure:Namely shiro The full class name of the exception class. 5 String exception = (String) request.getAttribute("shiroLoginFailure"); 6 7 System.out.println("exception=" + exception); 8 String msg = ""; 9 if (exception != null) { 10 if (UnknownAccountException.class.getName().equals(exception)) { 11 System.out.println("UnknownAccountException -- > No account exists:"); 12 msg = "Users do not exist!"; 13 } else if (IncorrectCredentialsException.class.getName().equals(exception)) { 14 System.out.println("IncorrectCredentialsException -- > The password is incorrect:"); 15 msg = "The password is incorrect!"; 16 } else if ("kaptchaValidateFailed".equals(exception)) { 17 System.out.println("kaptchaValidateFailed -- > Verification code error"); 18 msg = "Verification code error!"; 19 } else { 20 //msg = "else >> "+exception; 21 msg = "The password is incorrect!"; 22 System.out.println("else -- >" + exception); 23 } 24 } 25 redirectAttributes.addFlashAttribute("msg", msg); 26 session.setAttribute("msg", msg); 27 //return redirect("/login"); 28 return "redirect:login"; 29 //return msg; 30 }
At this time, the login authentication department has completed: one page + two background functions (one authentication function + one Login function)
2.2. Authority Management
Generally speaking, the privilege management only needs to add Shiro's privilege label to the interface. It can use the role label or the privilege label. Generally, the two labels are used together, and the effect is the best <@shiro.hasPermission name="xtgl-yhgl:read"> <@shiro.haslen name="xtgl-yhgl:read">

This project also completes the privilege management through this logic.

The two screenshots above represent a function.
So far, the authority authentication of Spring Boot integrated Shiro framework has been built, which can realize simple authority management.
3. Additional Documents
New files added to Shiro section compared with the previous blog
