Shiro study notes

Shiro core object

  1. 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

  2. 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.

  3. 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.

  4. 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.

  5. 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.

  6. 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);

Keywords: Spring Boot

Added by sastro on Sun, 02 Jan 2022 22:09:38 +0200