Source code for this article: GitHub. Click here || GitEE. Click here
1. Introduction to Shiro
1. Basic concepts
Apache Shiro is a powerful and easy-to-use Java security framework for authentication, authorization, password, and session management.Shiro is cleverly designed as a security framework.Shiro's application does not depend on any container; it can be used not only in JavaEE, but also in JavaSE environments.
2. Core Role
1) Subject: Authentication Subject
The user representing the current system is the user. In Shiro's authentication, the authentication principal is usually userName and passWord, or other unique identification related to the user.
2) SecurityManager: Security Manager
The core component of the Shiro architecture through which user authentication and authorization can be coordinated with other components.In fact, SecurityManager is the controller of the Shiro framework.
3) Realm: Domain Object
Defines how to access data to connect to different data sources, such as relational databases, configuration files, and so on.
3. Core Ideas
Shiro itself maintains users and permissions, completing user authentication and authorization through injection of Subject user principals and Realm domain objects.
2. Integrating the SpringBoot2 framework
1. Core Dependency
<dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>1.4.0</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.4.0</version> </dependency>
2. Shiro Core Configuration
@Configuration public class ShiroConfig { /** * Session Manager: session management * That is, the user logs in and then has a session, and all of its information is in the session before exiting. * Sessions can be in a normal JavaSE environment or a Web environment. */ @Bean("sessionManager") public SessionManager sessionManager(){ DefaultWebSessionManager sessionManager = new DefaultWebSessionManager(); //Set session expiration time sessionManager.setGlobalSessionTimeout(60 * 60 * 1000); sessionManager.setSessionValidationSchedulerEnabled(true); // Remove JSESSIONID from url at shiro login sessionManager.setSessionIdUrlRewritingEnabled(false); return sessionManager; } /** * SecurityManager: Security Manager */ @Bean("securityManager") public SecurityManager securityManager(UserRealm userRealm, SessionManager sessionManager) { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setSessionManager(sessionManager); securityManager.setRealm(userRealm); return securityManager; } /** * ShiroFilter Is the entry point for the entire Shiro, used to intercept requests requiring security control for processing */ @Bean("shiroFilter") public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) { ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean(); shiroFilter.setSecurityManager(securityManager); shiroFilter.setLoginUrl("/userLogin"); shiroFilter.setUnauthorizedUrl("/"); Map<String, String> filterMap = new LinkedHashMap<>(); filterMap.put("/userLogin", "anon"); shiroFilter.setFilterChainDefinitionMap(filterMap); return shiroFilter; } /** * Manage the life cycle of some bean s in Shiro */ @Bean("lifecycleBeanPostProcessor") public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() { return new LifecycleBeanPostProcessor(); } /** * Scan context for all Advistors * Apply these Advisor s to all Bean s that meet your starting point. */ @Bean public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() { DefaultAdvisorAutoProxyCreator proxyCreator = new DefaultAdvisorAutoProxyCreator(); proxyCreator.setProxyTargetClass(true); return proxyCreator; } /** * Match all methods with Shiro authentication annotations */ @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) { AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor(); advisor.setSecurityManager(securityManager); return advisor; } }
3. Domain Object Configuration
@Component public class UserRealm extends AuthorizingRealm { @Resource private SysUserMapper sysUserMapper ; @Resource private SysMenuMapper sysMenuMapper ; /** * Authorization (called when validating permissions) * Get User Rights Collection */ @Override public AuthorizationInfo doGetAuthorizationInfo (PrincipalCollection principals) { SysUserEntity user = (SysUserEntity)principals.getPrimaryPrincipal(); if(user == null) { throw new UnknownAccountException("Account does not exist"); } List<String> permsList; //The default user has the highest privileges List<SysMenuEntity> menuList = sysMenuMapper.selectList(); permsList = new ArrayList<>(menuList.size()); for(SysMenuEntity menu : menuList){ permsList.add(menu.getPerms()); } //User Rights List Set<String> permsSet = new HashSet<>(); for(String perms : permsList){ if(StringUtils.isEmpty(perms)){ continue; } permsSet.addAll(Arrays.asList(perms.trim().split(","))); } SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); info.setStringPermissions(permsSet); return info; } /** * Authentication (called at login) * Verify user login */ @Override protected AuthenticationInfo doGetAuthenticationInfo( AuthenticationToken authToken) throws AuthenticationException { UsernamePasswordToken token = (UsernamePasswordToken)authToken; //Query user information SysUserEntity user = sysUserMapper.selectOne(token.getUsername()); //Account does not exist if(user == null) { throw new UnknownAccountException("Incorrect account or password"); } //account lockout if(user.getStatus() == 0){ throw new LockedAccountException("Account is locked,Please contact your administrator"); } SimpleAuthenticationInfo info = new SimpleAuthenticationInfo (user, user.getPassword(), ByteSource.Util.bytes(user.getSalt()), getName()); return info; } @Override public void setCredentialsMatcher(CredentialsMatcher credentialsMatcher) { HashedCredentialsMatcher shaCredentialsMatcher = new HashedCredentialsMatcher(); shaCredentialsMatcher.setHashAlgorithmName(ShiroUtils.hashAlgorithmName); shaCredentialsMatcher.setHashIterations(ShiroUtils.hashIterations); super.setCredentialsMatcher(shaCredentialsMatcher); } }
4. Core Tool Classes
public class ShiroUtils { /** encryption algorithm */ public final static String hashAlgorithmName = "SHA-256"; /** Number of loops */ public final static int hashIterations = 16; public static String sha256(String password, String salt) { return new SimpleHash(hashAlgorithmName, password, salt, hashIterations).toString(); } // Get a test account admin public static void main(String[] args) { // 3743a4c09a17e6f2829febd09ca54e627810001cf255ddcae9dabd288a949c4a System.out.println(sha256("admin","123")) ; } /** * Get Session */ public static Session getSession() { return SecurityUtils.getSubject().getSession(); } /** * Subject: Principal, representing the current "user" */ public static Subject getSubject() { return SecurityUtils.getSubject(); } public static SysUserEntity getUserEntity() { return (SysUserEntity)SecurityUtils.getSubject().getPrincipal(); } public static Long getUserId() { return getUserEntity().getUserId(); } public static void setSessionAttribute(Object key, Object value) { getSession().setAttribute(key, value); } public static Object getSessionAttribute(Object key) { return getSession().getAttribute(key); } public static boolean isLogin() { return SecurityUtils.getSubject().getPrincipal() != null; } public static void logout() { SecurityUtils.getSubject().logout(); } }
5. Custom permission exception prompt
@RestControllerAdvice public class ShiroException { @ExceptionHandler(AuthorizationException.class) public String authorizationException (){ return "Sorry, you do not have permission to access this content!"; } @ExceptionHandler(Exception.class) public String handleException(Exception e){ return "System exception!"; } }
3. Case Demonstration Code
1. Test Interface
@RestController public class ShiroController { private static Logger LOGGER = LoggerFactory.getLogger(ShiroController.class) ; @Resource private SysMenuMapper sysMenuMapper ; /** * Logon Test * http://localhost:7011/userLogin?userName=admin&passWord=admin */ @RequestMapping("/userLogin") public void userLogin ( @RequestParam(value = "userName") String userName, @RequestParam(value = "passWord") String passWord){ try{ Subject subject = ShiroUtils.getSubject(); UsernamePasswordToken token = new UsernamePasswordToken(userName, passWord); subject.login(token); LOGGER.info("Login Successful"); }catch (Exception e) { e.printStackTrace(); } } /** * The server must request the above login interface before requesting it each time it restarts * http://localhost:7011/menu/list Get all menu lists * Permission requirements: sys:user:shiro */ @RequestMapping("/menu/list") @RequiresPermissions("sys:user:shiro") public List list(){ return sysMenuMapper.selectList() ; } /** * User does not have this permission and cannot access * Permission requirements: ccc:ddd:bbb */ @RequestMapping("/menu/list2") @RequiresPermissions("ccc:ddd:bbb") public List list2(){ return sysMenuMapper.selectList() ; } /** * Exit the test without any permission */ @RequestMapping("/userLogOut") public String logout (){ ShiroUtils.logout(); return "success" ; } }
2. Test process
1),Get permission after login http://localhost:7011/userLogin?userName=admin&passWord=admin 2),Access Rights Interface http://localhost:7011/menu/list 3),Access to unprivileged interfaces http://localhost:7011/menu/list2 4),Log out http://localhost:7011/userLogOut
4. Source code address
GitHub Address: Know a smile https://github.com/cicadasmile/middle-ware-parent Code Cloud Address: Know a smile https://gitee.com/cicadasmile/middle-ware-parent