Shiro core object
-
Subject is the subject. The external application interacts with the subject. The subject records the current operating user. The concept of user is understood as the subject of the current operation. It may be a user requested through the browser or a running program.
Subject is an interface in shiro, which defines many methods related to authentication and authorization. External programs are authenticated and authorized through subject, while subject is authenticated and authorized through SecurityManager security manager -
SecurityManager
SecurityManager is the security manager, which manages all subjects. It is the core of shiro and is responsible for the security management of all subjects. subject authentication and authorization can be completed through SecurityManager. In essence, SecurityManager authenticates through Authenticator, authorizes through Authorizer, and manages sessions through SessionManager. -
Authenticator
Authenticator, that is, authenticator, authenticates the user's identity. Authenticator is an interface. shiro provides the ModularRealmAuthenticator implementation class. Most requirements can be basically met through ModularRealmAuthenticator, or the authenticator can be customized. -
The authorizer is the authorizer. The user passes the authentication through the authenticator. When accessing the function, the authorizer needs to judge whether the user has the operation authority of this function.
-
realm
Realm is the domain, which is equivalent to the datasource data source. For security authentication, the securityManager needs to obtain the user permission data through realm. For example, if the user identity data is in the database, realm needs to obtain the user identity information from the database.
Note: don't interpret realm as just fetching data from the data source. There are relevant codes for authentication and authorization verification in realm. -
sessionManager
Session manager is session management. shiro framework defines a set of session management, which does not depend on the session of the web container. Therefore, shiro can be used in non web applications or manage the sessions of distributed applications at one point. This feature enables it to achieve single sign on.
Shiro dependency
<dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.4.0</version> </dependency>
Shiro configuration
@Bean(name = "shiroFilter") public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); //Shiro's core security interface, this attribute is required shiroFilterFactoryBean.setSecurityManager(securityManager); //When the request is intercepted, go to Ajax permissions authorizationfilter () for processing //Go to Ajax permissions authorizationfilter is a custom class that inherits from FormAuthenticationFilter Map<String, Filter> filterMap = new LinkedHashMap<>(); filterMap.put("authc", new AjaxPermissionsAuthorizationFilter()); shiroFilterFactoryBean.setFilters(filterMap); /*Define shiro filter chain Map structure * Map The path represented by the first '/' of key (value in XML) is relative to HttpServletRequest From the value of getcontextpath() * anon: Its corresponding filter is empty and does nothing here do and The * after JSP indicates parameters, such as login jsp? Main this * authc: The page under this filter can only be accessed after verification. It is a built-in interceptor in Shiro. Org apache. shiro. web. filter. authc. FormAuthenticationFilter */ Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>(); /* The filter chain definition is executed from top to bottom, and / * * is generally placed at the bottom: This is a pit, and the code is difficult to use if you are not careful; authc:All URLs must be authenticated before they can be accessed; anon: all URLs can be accessed anonymously */ filterChainDefinitionMap.put("/", "anon"); filterChainDefinitionMap.put("/static/**", "anon"); filterChainDefinitionMap.put("/login/auth", "anon"); filterChainDefinitionMap.put("/login/getCode", "anon"); filterChainDefinitionMap.put("/login/logout", "anon"); filterChainDefinitionMap.put("/error", "anon"); filterChainDefinitionMap.put("/swagger-ui.html", "anon"); filterChainDefinitionMap.put("/doc.html", "anon"); filterChainDefinitionMap.put("/swagger-resources/**", "anon"); filterChainDefinitionMap.put("/v2/**", "anon"); filterChainDefinitionMap.put("/webjars/**", "anon"); filterChainDefinitionMap.put("/**", "authc"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); return shiroFilterFactoryBean; } //Configure securityManager @Bean public SecurityManager securityManager() { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); //Set Realm here securityManager.setRealm(userRealm()); return securityManager; } //Configure userRealm. If login encryption is to be used, configure setCredentialsMatcher @Bean public UserRealm userRealm() { UserRealm userRealm = new UserRealm(); userRealm.setCredentialsMatcher(hashedCredentialsMatcher()); return userRealm; } //Configure encryption rules @Bean() public HashedCredentialsMatcher hashedCredentialsMatcher() { HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher(); //Hash algorithm: MD5 algorithm is used here; hashedCredentialsMatcher.setHashAlgorithmName("md5"); //The number of hashes, such as hashing twice, is equivalent to md5(md5("")); hashedCredentialsMatcher.setHashIterations(2); //storedCredentialsHexEncoded is true by default. In this case, the password is used, and the Hex code is used for encryption; Base64 encoding is used when false hashedCredentialsMatcher.setStoredCredentialsHexEncoded(true); return hashedCredentialsMatcher; }
Custom realm
public class UserRealm extends AuthorizingRealm { private Logger logger = LoggerFactory.getLogger(UserRealm.class); //The logged in service is the dao interface, which is used to obtain database user information @Autowired private LoginService loginService; @Autowired private UserService userService; @Override @SuppressWarnings("unchecked") protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { //Get user SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo(); String primaryPrincipal = (String)principals.getPrimaryPrincipal(); System.out.println("Call permission verification:" + primaryPrincipal); //Get user information JSONObject user = loginService.getUser(primaryPrincipal); String role = (String) user.get("role_name"); //Add roles for users authorizationInfo.addRole(role); //Returns the authorizationInfo return authorizationInfo; } /** * Verify the currently logged in Subject * LoginController.login()Method Execute this method when login() */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException { String loginName = (String) authcToken.getPrincipal(); // Get user password String password = new String((char[]) authcToken.getCredentials()); JSONObject user = loginService.getUser(loginName); if (user == null) { //No account found throw new UnknownAccountException(); } user.put("roleName",loginService.getUser(loginName).get("role_name")); //Give it to AuthenticatingRealm and use the CredentialsMatcher for password matching. If you think others are bad, you can customize the implementation SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo( user.getString("login_name"), user.getString("password"), ByteSource.Util.bytes(user.getString("salt")), getName() ); //There is no need to save the password in the session user.remove("password"); SecurityUtils.getSubject().getSession().setAttribute(Constants.SESSION_USER_INFO, user); return authenticationInfo; } }
Write the login logic of the Controller
public JSONObject authLogin(JSONObject jsonObject) { String username = jsonObject.getString("loginName"); String password = jsonObject.getString("password"); String code = jsonObject.getString("code"); JSONObject data = new JSONObject(); Subject currentUser = SecurityUtils.getSubject(); UsernamePasswordToken token = new UsernamePasswordToken(username, password); //Judgment verification code if(code == null || code.equals("")){ return CommonUtil.failJson(data); } String relcode = (String)currentUser.getSession().getAttribute("code"); if(relcode == null || relcode.equals("")){ data.put("code", "201"); data.put("msg", "Verification code failure"); return data; } if(!relcode.equals(relcode)){ data.put("code", "201"); data.put("msg", "Verification code error"); return data; } try { //When it is submitted to shiro for verification, the essence is to call the logic in UserRealm. If it fails, an exception will be thrown. currentUser.login(token); data.put("code", "200"); data.put("msg", "Request succeeded"); } catch (AuthenticationException e) { data.put("code", "201"); data.put("msg", "Wrong account or password"); } return data; }
Register encryption logic
If encryption is adopted, the password shall be encrypted and stored in the database during registration
String originalPassword = user.getPassword(); //Original password String hashAlgorithmName = "md5"; //Encryption mode int hashIterations = 2; //Number of encryption //Random salt String salt = new SecureRandomNumberGenerator().nextBytes().toHex(); //encryption SimpleHash simpleHash = new SimpleHash(hashAlgorithmName, originalPassword, salt, hashIterations); //Encrypted password String encryptionPassword = simpleHash.toString(); //Save to user object user.setSalt(salt); user.setPassword(encryptionPassword); //... Other logic //Save to database userDao.addUser(user);