Shiro
apache shiro is a lightweight authentication and authorization framework. Compared with spring security, it is simple and easy to use and has high flexibility. Springboot itself provides support for security, after all, it is its own thing. Springboot does not integrate shiro for the time being, so you have to match it yourself.
1. Add dependencies
<dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.2.5</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-ehcache</artifactId> <version>1.2.5</version> </dependency>
2. Write Shiro configuration class
package com.xbz.web.system.config; import at.pollux.thymeleaf.shiro.dialect.ShiroDialect; import org.apache.shiro.authc.credential.CredentialsMatcher; import org.apache.shiro.authc.credential.HashedCredentialsMatcher; import org.apache.shiro.cache.ehcache.EhCacheManager; import org.apache.shiro.codec.Base64; import org.apache.shiro.session.SessionListener; import org.apache.shiro.session.mgt.SessionManager; import org.apache.shiro.session.mgt.eis.MemorySessionDAO; import org.apache.shiro.session.mgt.eis.SessionDAO; import org.apache.shiro.spring.LifecycleBeanPostProcessor; import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor; import org.apache.shiro.spring.web.ShiroFilterFactoryBean; import org.apache.shiro.web.mgt.CookieRememberMeManager; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; import org.apache.shiro.web.servlet.SimpleCookie; import org.apache.shiro.web.session.mgt.DefaultWebSessionManager; import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator; import org.springframework.boot.autoconfigure.condition.Condition.ConditionalOnMissingBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.DependsOn; import java.util.ArrayList; import java.util.Collection; import java.util.LinkedHashMap; import java.util.Map; /** * shiro configuration class* ApacheShiro core implements permission control and interception through Filter, just like SpringMVC uses DispachServlet to control request distribution. * Since it is using Filter, that is, filtering and permission verification through URL rules, we need to define a series of rules and access rights about URLs*/ @Configuration public class ShiroConfiguration { /** * DefaultAdvisorAutoProxyCreator, a bean of Spring, and Advisor decides which classes of methods to AOP proxy. */ @Bean @ConditionalOnMissingBean public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() { DefaultAdvisorAutoProxyCreator defaultAAP = new DefaultAdvisorAutoProxyCreator(); defaultAAP.setProxyTargetClass(true); return defaultAAP; } /** * ShiroFilterFactoryBean: In order to generate ShiroFilter, handle intercepting resource files. * It mainly maintains three items of data, securityManager, filters, filterChainDefinitionManager. * Note: A single ShiroFilterFactoryBean configuration is or reports an error, Because when initializing ShiroFilterFactoryBean, it is necessary to inject: SecurityManager * * FilterChain definition description* 1. A URL can configure multiple Filters, separated by commas * 2. When multiple filters are set, all of them are verified and passed, and they are considered to be passed * 3. Some filters can specify parameters, such as perms, roles * */ @Bean public ShiroFilterFactoryBean shiroFilterFactoryBean() { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); shiroFilterFactoryBean.setSecurityManager(securityManager()); shiroFilterFactoryBean.setLoginUrl("/login");//Do not set the login.jsp page under the root directory of the web project shiroFilterFactoryBean.setSuccessUrl("/index");//The connection to jump after login is successful shiroFilterFactoryBean.setUnauthorizedUrl("/403");//Unauthorized page jump/* // Custom interceptor, settings for multiple filters*// Map<String, Filter> filters = new LinkedHashMap<>(); // LogoutFilter logoutFilter = new LogoutFilter();//Limit the number of online numbers of the same account at the same time. or single sign-on, etc. // logoutFilter.setRedirectUrl("/login"); // filters.put("logout",null); // shiroFilterFactoryBean.setFilters(filters); Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>(); //filterChainDefinitionManager must be a LinkedHashMap because it must ensure orderly filterChainDefinitionMap.put("/css/**", "anon"); // Static resources do not require permissions, If there are files in other directories (such as js, img, etc.) and filterChainDefinitionMap.put("/", "anon"); filterChainDefinitionMap.put("/login", "anon"); //Configure the part of url filterChainDefinitionMap.put("/logout", "logout"); filterChainDefinitionMap.put("/user/**", "authc,roles[ROLE_USER]"); //The user is a ROLE_USER role to access. The user role controls the user behavior. filterChainDefinitionMap.put("/events/**", "authc,roles[ROLE_ADMIN]"); // filterChainDefinitionMap.put("/user/edit/**", "authc,perms[user:edit]"); // For testing, the dead value is fixed, and it can also be read from the database or other configurations. Here, the permissions are used to control filterChainDefinitionMap.put("/**", "authc"); // Resources that need to be logged in, generally put /** at the bottom shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); return shiroFilterFactoryBean; } //region cookies and Session // ========================== Cookie and Session Management begin ========================= private static final String COOKIE_NAME = "rememberMe"; /** cookie object management*/ public SimpleCookie rememberMeCookie(){ //This parameter is the name of the cookie, corresponding to the name of the checkbox on the front end = rememberMe SimpleCookie simpleCookie = new SimpleCookie(COOKIE_NAME); simpleCookie.setMaxAge(604800);//Remember my cookie takes effect for 7 days, and the unit is returned simpleCookie; } /** cookie management object: Remember me function*/ public CookieRememberMeManager rememberMeManager(){ CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager(); cookieRememberMeManager.setCookie(rememberMeCookie()); cookieRememberMeManager.setCipherKey(Base64.decode("3AvVhmFLUs0KTA3Kprsdag=="));//RememberMe cookie encryption key suggests that each item is different Default AES algorithm key length (128 256 512 bits) return cookieRememberMeManager; } @Bean SessionDAO sessionDAO() { return new MemorySessionDAO(); } @Bean public SessionManager sessionManager() { DefaultWebSessionManager sessionManager = new DefaultWebSessionManager(); Collection<SessionListener> listeners = new ArrayList<>(); listeners.add(new BDSessionListener()); sessionManager.setSessionListeners(listeners); sessionManager.setSessionDAO(sessionDAO()); return sessionManager; } // ===================== Cookies and Session Management end =======================/endregion /** * SecurityManager: Core security transaction manager, permission management* This class combines login, logout, permissions, and session processing. It is a relatively important class. */ @Bean(name = "securityManager") public DefaultWebSecurityManager securityManager() { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setRealm(shiroRealm()); securityManager.setCacheManager(ehCacheManager()); /////User Authorization/authentication information Cache, using EhCache cache// Custom session management uses redis securityManager.setSessionManager(sessionManager()); //Inject Remember my manager; securityManager.setRememberMeManager(rememberMeManager()); return securityManager; } /** * ShiroRealm , this is a custom authentication class, inherited from AuthorizingRealm , * Responsible for user authentication and permission processing, you can refer to the implementation of JdbcRealm. */ @Bean @DependsOn("lifecycleBeanPostProcessor") public ShiroRealm shiroRealm(CredentialsMatcher matcher) { ShiroRealm realm = new ShiroRealm(); realm.setCredentialsMatcher(matcher);// Password verification implements return realm; } /** * EhCacheManager , CacheManager* After the user logs in successfully, caches the user information and permission information, and then every time the user requests, put it into the user's session. If this bean is not set, the database will be queried once for each request. */ @Bean @DependsOn("lifecycleBeanPostProcessor") public EhCacheManager ehCacheManager() { EhCacheManager em = new EhCacheManager(); em.setCacheManagerConfigFile("classpath:config/ehcache.xml");//Configuration file path return em; } /** * LifecycleBeanPostProcessor, this is a subclass of DestructionAwareBeanPostProcessor, * is responsible for the life cycle of org.apache.shiro.util.Initializable type bean. * is mainly a subclass of AuthorizingRealm class, as well as the EhCacheManager class. */ @Bean(name = "lifecycleBeanPostProcessor") public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() { return new LifecycleBeanPostProcessor(); } /** * HashedCredentialsMatcher , This class is for encoding the password, * preventing the passwords from being stored in the database clearly. Of course, when logging in to authentication, * This class is also responsible for encoding the passwords entered in the form* Processing authentication matching processor: If customization requires the inheritance of HashedCredentialsMatcher */ @Bean(name = "hashedCredentialsMatcher") public HashedCredentialsMatcher hashedCredentialsMatcher() { HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher(); credentialsMatcher.setHashAlgorithmName("MD5");//Specify the encryption method, and you can also add the cache here. When the user logs in more than five login errors, the user is locked. The user is prohibited from constantly trying to log in credentialsMatcher.setHashIterations(2); credentialsMatcher.setStoredCredentialsHexEncoded(true); return credentialsMatcher; } /** * AuthorizationAttributeSourceAdvisor , the Advisor class implemented in shiro, * Use AopAllianceAnnotationsAuthorizingMethodInterceptor internally to intercept methods with the following annotations. */ @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() { AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor(); advisor.setSecurityManager(securityManager()); return advisor; } // @Bean public ShiroDialect shiroDialect() { return new ShiroDialect(); } } 3. Customize Realm verification class
package com.yiyun.web.system.config; import com.yiyun.dao.master.UserDao; import com.yiyun.domain.UserDO; import com.yiyun.web.common.utils.ShiroUtils; import com.yiyun.web.system.service.MenuService; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.*; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.authz.SimpleAuthorizationInfo; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.session.Session; import org.apache.shiro.subject.PrincipalCollection; import org.springframework.beans.factory.annotation.Autowired; import java.util.*; /** * Get user role and permission information*/ public class ShiroRealm extends AuthorizingRealm { // Generally, what is written here is servic @Autowired private UserDao userMapper; @Autowired private MenuService menuService; /** * Login authentication Generally speaking, after logging in, the current user is granted permissions. This step is based on doGetAuthenticationInfo. Only after user information can you determine whether to authorize the user based on the user's role and authorization information. Therefore, Roles and Permissions here are the two key judgment basis for the user* @param authenticationToken * @return * @throws AuthenticationException */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken; UserDo user = userMapper.findByName(token.getUsername());// Check if there is this user if(user != null){ // If there is, store this user in the login authentication info, and there is no need to compare the password yourself. Shiro will perform password comparison and verification for us List<URole> rlist = uRoleDao.findRoleByUid(user.getId());//Get the user role List<UPermission> plist = uPermissionDao.findPermissionByUid(user.getId());//Get user permission List<String> roleStrlist=new ArrayList<String>();////User's role collection List<String> permissionStrlist=new ArrayList<String>();///User's permission collection for (URole role: rlist) { roleStrlist.add(role.getName()); } for (UPermission uPermission: plist) { perminsStrlist.add(uPermission.getName()); } user.setRoleStrlist(roleStrlist); user.setPerminsStrlist(perminsStrlist); Session session = SecurityUtils.getSubject().getSession(); session.setAttribute("user", user);// If successful, put it in session // If it exists, store this user in the login authentication info, and you don't need to compare your password yourself. Shiro will perform password comparison verification for us. Return new SimpleAuthenticationInfo(user, user.getPassword(), getName()); } return null; } /** * Permission authentication* Get the user's permission information, which is to make judgments for the next step of authorization, to obtain the current user's role and the permission information owned by these roles* @param principalCollection * @return */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { // Get the user name entered in the current login, which is equivalent to (String) principalCollection.fromRealm(getName()).iterator().next(); // String loginName = (String) super.getAvailablePrincipal(principalCollection); UserDo user = (UserDo) principalCollection.getPrimaryPrincipal(); // // Go to the database to check whether this object is present// User user = null;// In actual projects, you can cache according to the actual situation. If you do not, Shiro himself has a time interval mechanism and will not execute the method repeatedly within 2 minutes // user = userMapper.findByName(loginName); if (user != null) { //Permission information object info, is used to store all the roles (role) and permissions (permission) of the found user SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); //User's role collection info.addRoles(user.getRoleStrlist()); //User's permission collection info.addStringPermissions(user.getPerminsStrlist()); return info; } // Return null, it will cause any user to access the intercepted request to automatically jump to the address specified by unauthorizedUrl; } }4. Finally, take a look at the configuration file of ehcache
<?xml version="1.0" encoding="UTF-8"?> <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd" updateCheck="false"> <diskStore path="java.io.tmpdir/Tmp_EhCache" /> <!-- name:Cache name. maxElementsInMemory: Maximum number of caches maxElementsOnDisk: Maximum number of caches for hard disk. eternal: Whether the object is permanently valid, once set, timeout will not work. overflowToDisk: Whether to save to disk, timeToIdleSeconds: Sets the allowed idle time (unit: seconds) of the object before it expires. Used only if the eternal=false object is not permanently valid, optional attribute, the default value is 0, which means the idle time is infinite. timeToLiveSeconds: Sets the time (unit: seconds) of the object before it expires. The maximum time is between the creation time and the failure time. Only used if the eternal=false object is not permanently valid, the default is 0., which means that the object's survival time is infinite. diskPersistent: Whether the disk store persists between restarts of the Virtual Machine. The default value is false. diskSpoolBufferSizeMB: This parameter sets the cache size of the DiskStore (disk cache). The default is 30MB. Each cache should have its own buffer. diskExpiryThreadIntervalSeconds: The disk failure thread run time interval, the default is 120 seconds. memoryStoreEvictionPolicy: When the maxElementsInMemory limit is reached, Ehcache will clean up memory according to the specified policy. The default policy is LRU (most recently used). You can set it to FIFO (first in, first out) or LFU (less used). clearOnFlush: Whether to clear when the amount of memory is maximum. memoryStoreEvictionPolicy: Three clearing strategies for Ehcache; FIFO, first in first out, this is the most familiar to everyone, first in first out. LFU, Less Frequently Used, is the strategy used in the above example. To be blunt, it is to say that it has been the least used ever. As mentioned above, the cached element has a hit attribute, and the lowest hit value will be cleared out of the cache. LRU, Least Recently Used, the cached element has a timestamp. When the cache capacity is full and you need to make room to cache new elements, the element with the farthest time in the existing cache element will be cleared out of the cache. --> <defaultCache eternal="false" maxElementsInMemory="1000" overflowToDisk="false" diskPersistent="false" timeToIdleSeconds="0" timeToLiveSeconds="600" memoryStoreEvictionPolicy="LRU" /> <!-- Login record cache lock for 10 minutes--> <cache name="passwordRetryCache" maxEntriesLocalHeap="2000" eternal="false" timeToIdleSeconds="3600" timeToLiveSeconds="0" overflowToDisk="false" statistics="true"> </cache> </ehcache>
The above is all the content of this article. I hope it will be helpful to everyone's learning and I hope everyone will support Wulin.com more.