preface

This article mainly explains the following knowledge points:

  • A brief introduction to Shiro’s licensing methods
  • With Spring integration
  • Initial Shiro filter

I. Shiro authorization

We covered Shiro’s authentication in the last article, now let’s deal with Shiro’s authorization

Shiro’s licensing process is similar to the certification process:

1.1 Authorization methods supported by Shiro

Shiro supports three types of authorization:

Shiro supports authorization in three ways: (...) Programing is done by writing if/else authorization blocks: Subject Subject = securityutils.getSubject (); 3. Annotated: Done by placing appropriate annotations on the executing Java method: @requiresroles ("admin") public void hello() {// Have rights}... > </shiro:hasRole>Copy the code

1.2 Use programmatic authorization

Similarly, we are authorized through the security manager, so we still need to configure the corresponding configuration file:

Shro-permission. ini configuration file:

# user [users] # user zhang's password is 123, This user has role1 and role2 two roles zhang = 123, role1, role2 wang = 123, role2 # # access [roles] role role1 have the create, update the resource user permissions Role1 =user:create,user:update # Role role2 has the create and delete permissions on resource user Role3 =user:create # Permission identifier rules: Resource: Operation: instance (separated by semicolons) User :create :01 Indicates that the create operation is performed on instance 01 of user resources. User :create: The create operation is performed on user resources, which is equivalent to user:create:*. The create operation is performed on all user resource instances. User: * : 01 Indicates that all operations are performed on user resource instance 01.Copy the code

Code test:



	// Role authorization and resource authorization tests
	@Test
	public void testAuthorization(a) {

		Create a SecurityManager factory
		Factory<SecurityManager> factory = new IniSecurityManagerFactory(
				"classpath:shiro-permission.ini");

		/ / create the SecurityManager
		SecurityManager securityManager = factory.getInstance();

		// Set the SecurityManager to the system runtime environment, and then configure the SecurityManager in the Spring container
		SecurityUtils.setSecurityManager(securityManager);

		/ / create the subject
		Subject subject = SecurityUtils.getSubject();

		// Create a token
		UsernamePasswordToken token = new UsernamePasswordToken("zhangsan"."123");

		// Perform authentication
		try {
			subject.login(token);
		} catch (AuthenticationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		System.out.println("Certification Status:" + subject.isAuthenticated());
		// Perform authorization after the authentication succeeds

		// Role-based authorization
		// hasRole passes the role identity
		boolean ishasRole = subject.hasRole("role1");
		System.out.println("Single role judgment" + ishasRole);
		// hasAllRoles has multiple roles
		boolean hasAllRoles = subject.hasAllRoles(Arrays.asList("role1"."role2"."role3"));
		System.out.println("Multiple role judgment" + hasAllRoles);

		// Use the check method for authorization. If authorization fails, an exception will be thrown
		// subject.checkRole("role13");

		// Resource-based authorization
		// isPermitted passes the permission identifier
		boolean isPermitted = subject.isPermitted("user:create:1");
		System.out.println("Single permission judgment" + isPermitted);

		boolean isPermittedAll = subject.isPermittedAll("user:create:1"."user:delete");
		System.out.println("Multiple permission judgment" + isPermittedAll);
		// Use the check method for authorization. If authorization fails, an exception will be thrown
		subject.checkPermission("items:create:1");

	}

Copy the code

1.3 Customize Realm for Authorization

Typically, our permissions are queried from the database, not paired against our profile. Therefore, we need to customize REAML, and let REAML compare the permissions of the database query

Shiro-realm. ini configuration file: Injects custom REAML information into the security manager

[the main] # custom realm customRealm = cn. Itcast. Shiro. Realm. CustomRealm # set the realm to the securityManager, Equivalent to injecting SecurityManager.Realms =$customRealm in SpringCopy the code

We already used a custom reAML last time, when we just overwrote the doGetAuthenticationInfo() method. This time we’ll override the doGetAuthorizationInfo() method

	// Used for authorization
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo( PrincipalCollection principals) {
		
		// Get identity information from the principals
		// Convert the return value of the getPrimaryPrincipal method to the true identity type (the doGetAuthenticationInfo authentication above fills in the identity type to SimpleAuthenticationInfo),
		String userCode =  (String) principals.getPrimaryPrincipal();
		
		// Obtain permission information based on identity information
		// Connect to database...
		// Simulate getting data from the database
		List<String> permissions = new ArrayList<String>();
		permissions.add("user:create");// Create a user
		permissions.add("items:add");// Add permissions to goods
		//....
		
		// Check permissions for permissions.
		SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
		// Populate the simpleAuthorizationInfo object with the authorization information queried above
		simpleAuthorizationInfo.addStringPermissions(permissions);

		return simpleAuthorizationInfo;
	}

Copy the code

Test procedure:


	// Customize a realm for resource authorization tests
	@Test
	public void testAuthorizationCustomRealm(a) {

		Create a SecurityManager factory
		Factory<SecurityManager> factory = new IniSecurityManagerFactory(
				"classpath:shiro-realm.ini");
		/ / create the SecurityManager
		SecurityManager securityManager = factory.getInstance();
		// Set the SecurityManager to the system runtime environment, and then configure the SecurityManager in the Spring container
		SecurityUtils.setSecurityManager(securityManager);
		/ / create the subject
		Subject subject = SecurityUtils.getSubject();

		// Create a token
		UsernamePasswordToken token = new UsernamePasswordToken("zhangsan"."111111");
		// Perform authentication
		try {
			subject.login(token);
		} catch (AuthenticationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		System.out.println("Certification Status:" + subject.isAuthenticated());
		// Perform authorization after the authentication succeeds

		// For resource-based authorization, calling the isPermitted method calls CustomRealm to query the correct permission data from the database
		// isPermitted pass the permission identifier to determine whether user:create:1 is in the CustomRealm query
		boolean isPermitted = subject.isPermitted("user:create:1");
		System.out.println("Single permission judgment" + isPermitted);

		boolean isPermittedAll = subject.isPermittedAll("user:create:1"."user:create");
		System.out.println("Multiple permission judgment" + isPermittedAll);

		// Use the check method for authorization. If authorization fails, an exception will be thrown
		subject.checkPermission("items:add:1");

	}
Copy the code


Integration of Spring and Shiro

2.1 Importing jar Packages

  • Shiro – web jar,
  • Shiro – spring jar
  • Shiro jar – code

2.2 Quick Start

Shiro also intercepts through filter. Filter intercepts and gives control to the filterChain configured in Spring.

Configure filter in web. XML


<! -- Shiro filter -->
	<! -- Shiro filter, DelegatingFilterProxy associate bean and filter in spring container using proxy mode -->
	<filter>
		<filter-name>shiroFilter</filter-name>
		<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
		<! -- Set true to filter lifecycle controlled by servlet container -->
		<init-param>
			<param-name>targetFilterLifecycle</param-name>
			<param-value>true</param-value>
		</init-param>
		<! < bean name > < bean name > < bean name >
		<init-param>
			<param-name>targetBeanName</param-name>
			<param-value>shiroFilter</param-value>
		</init-param>
	</filter>
	<filter-mapping>
		<filter-name>shiroFilter</filter-name>
		<url-pattern>/ *</url-pattern>
	</filter-mapping>
Copy the code

In applicationContext-shro.xml, configure the beans in the Spring container corresponding to fitler in web.xml.


<! -- Bean corresponding to Shiro's filter in web.xml -->
<! -- Shiro's Web filter -->
	<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
		<property name="securityManager" ref="securityManager" />
		<! -- loginUrl authentication submission address. If there is no authentication, this address will be requested for authentication. This address will be authenticated by the formAuthenticationFilter -->
		<property name="loginUrl" value="/login.action" />
		<! Shiro will automatically go to the last request path if the authentication succeeds.
		<! -- <property name="successUrl" value="/first.action"/> -->
		<! -- Use unauthorizedUrl to jump to page without permission -->
		<property name="unauthorizedUrl" value="/refuse.jsp" />
		<! -- Custom filter configuration -->
		<property name="filters">
			<map>
				<! Add custom FormAuthenticationFilter to shiroFilter -->
				<entry key="authc" value-ref="formAuthenticationFilter" />
			</map>
		</property>
		
		<! -- Filter chain definition, executed from top to bottom, usually with /** at the bottom
		<property name="filterChainDefinitions">
			<value>
				<! All urls can be accessed anonymously -->
				/** = anon
			</value>
		</property>
	</bean>
Copy the code

Configure the security manager


<! -- securityManager -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
		<property name="realm" ref="customRealm" />	
</bean>
Copy the code

Configuration reaml


<! -- realm -->
<bean id="customRealm" class="cn.itcast.ssm.shiro.CustomRealm">
</bean>
Copy the code

Steps:

  • Configure Shiro’s filters in the web.xml file
  • Configure the corresponding filterChain in the corresponding Spring configuration file
  • Configure the security manager to inject custom REAML
  • Configure custom REAML

2.3 Static Resources Are not Intercepted

When we configure the filter chain in Spring, we find this line of code:

	<! All urls can be accessed anonymously -->
 	/** = anon
Copy the code

Anon is essentially shiro’s built-in filter, and the code above represents that all anonymous users can access it

Of course, we need to configure other information later, in order for the page to display properly, our static resources generally do not need to be intercepted.

So we can configure it like this:


	<! Set anonymous access to static resources -->
	/images/** = anon
	/js/** = anon
	/styles/** = anon
Copy the code

First understanding of Shiro filter

Above we learned about anno filter, Shiro filter and other filters.. Let’s see

Commonly used filters are as follows:

Anon: example /admins/**= Anon has no parameter and can be used anonymously. /admins/user/**=authc: /admins/user/**=authc: /admins/user/**=authc For example, /admins/user/**=perms[user:add:*], multiple parameters can be quoted and separated by commas. For example, /admins/user/**=perms[“user:add:*,user:modify:*”], When there are multiple arguments, each argument must be passed, like the isPermitedAll() method. User: for example, /admins/user/**=user. If no parameter is specified, the user must exist

3.1 Login and Logout

Using the FormAuthenticationFilter filter, the principle is as follows:

  • When the user is not authenticated, loginURL is requested for authentication (which we have configured above), and the user identity and user password are submitted to loginURL
  • The FormAuthenticationFilter intercepts username and password in the fetch request (the two parameter names can be configured)
  • FormAuthenticationFilter calls realm passing in a token (username and password)
  • Realm authentication queries user information (stored in Activeuser, including userid, usercode, username, and menus) based on the username.
  • If not found, the realm returns NULL, and the FormAuthenticationFilter populates the request field with a parameter.
  • After querying the user’s information, the FormAuthenticationFilter automatically compares the information returned by REAML with the user name and password in the token. If not, return an exception.

3.1.1 Login page

Because of the default values (username and password) of the input of the user’s identity and password in the FormAuthenticationFilter, change the name of the input of the page’s account and password to username and password


	<TR>
		<TD>User name:</TD>
		<TD colSpan="2"><input type="text" id="usercode"
			name="username" style="WIDTH: 130px" /></TD>
	</TR>
	<TR>
		<TD>The secret code:</TD>
		<TD><input type="password" id="pwd" name="password" style="WIDTH: 130px" />
		</TD>
	</TR>
Copy the code

3.1.2 Login code implementation

As mentioned above, when the user is not authenticated, the requested LoginURL is authenticated, and the user password of the user’s identity is submitted to loginrul.

When we submit to loginURL, the form filter automatically parses username and password to call realm for authentication. Finally, the shiroLoginFailure authentication information is stored in the Request domain object. If an exception is returned, then we can throw an exception in login



// Log in to the submission address, which is the same as the loginURL configured in applicationContext-shro.xml
	@RequestMapping("login")
	public String login(HttpServletRequest request)throws Exception{
		
		// shiroLoginFailure is the fully qualified name of Shiro's exception class if authentication exception information is obtained from request if login fails
		String exceptionClassName = (String) request.getAttribute("shiroLoginFailure");
		// Throw the specified exception based on the exception classpath returned by Shiro
		if(exceptionClassName! =null) {if (UnknownAccountException.class.getName().equals(exceptionClassName)) {
				// The exception handler is finally thrown
				throw new CustomException("Account does not exist");
			} else if (IncorrectCredentialsException.class.getName().equals(
					exceptionClassName)) {
				throw new CustomException("Wrong username/password");
			} else if("randomCodeError".equals(exceptionClassName)){
				throw new CustomException("Verification code error");
			}else {
				throw new Exception();// An unknown error is eventually generated in the exception handler}}// This method does not handle login success (authentication success), Shiro authentication success automatically jumps to the last request path
		// Failed login returns to the login page
		return "login";
	}
Copy the code

Configuring authentication Filters


	<value>
		<! Set anonymous access to static resources -->
		/images/** = anon
		/js/** = anon
		/styles/** = anon

		<! -- /** = authc -->
		/** = authc
	</value>

Copy the code

3.2 quit

We do not need to implement the exit, just access an exit URL (the URL may not exist), the LogoutFilter intercepts, clear the session.

Configure LogoutFilter in applicationContext-shro.xml:


		<! Logout. Action address, shiro to clear session
		/logout.action = logout
Copy the code

4. Information will be displayed on the page after authentication

1. After authentication, the user menu is displayed on the home page. 2

Realm queries user information from the database, setting the user menu, Usercode, username, and so on in SimpleAuthenticationInfo.


	// Realm authentication method, query user information from database
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo( AuthenticationToken token) throws AuthenticationException {
		
		// Token is the user name and password entered by the user
		// The first step is to retrieve the user name from the token
		String userCode = (String) token.getPrincipal();

		// Step 2: query the database according to the userCode entered by the user
		SysUser sysUser = null;
		try {
			sysUser = sysService.findSysUserByUserCode(userCode);
		} catch (Exception e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}

		// Return null if no query is found
		if(sysUser==null) {//
			return null;
		}
		// Query the password from the database
		String password = sysUser.getPassword();
		
		/ / salt
		String salt = sysUser.getSalt();

		// If found, the authentication information AuthenticationInfo is returned
		
		//activeUser is the user identity information
		ActiveUser activeUser = new ActiveUser();
		
		activeUser.setUserid(sysUser.getId());
		activeUser.setUsercode(sysUser.getUsercode());
		activeUser.setUsername(sysUser.getUsername());
		/ /..
		
		// Retrieve the menu according to the user ID
		List<SysPermission> menus  = null;
		try {
			// Fetch the menu through service
			menus = sysService.findMenuListByUserId(sysUser.getId());
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		// Set the user menu to activeUser
		activeUser.setMenus(menus);

		// Set activeUser to simpleAuthenticationInfo
		SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(
				activeUser, password,ByteSource.Util.bytes(salt), this.getName());

		return simpleAuthenticationInfo;
	}
Copy the code

Configure by allocator, because we used MD5 and hashing


<! -- Certificate matcher -->
<bean id="credentialsMatcher"
	class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
	<property name="hashAlgorithmName" value="md5" />
	<property name="hashIterations" value="1" />
</bean>
Copy the code

<! -- realm -->
<bean id="customRealm" class="cn.itcast.ssm.shiro.CustomRealm">
	<! Realm hashes the credential matcher as required -->
	<property name="credentialsMatcher" ref="credentialsMatcher"/>
</bean>
Copy the code

When jumping to the home page, take out the user’s authentication information, forward to JSP


	//
	@RequestMapping("/first")
	public String first(Model model)throws Exception{
		
		// Get the activeUser from Shiro's session
		Subject subject = SecurityUtils.getSubject();
		// Get the identity information
		ActiveUser activeUser = (ActiveUser) subject.getPrincipal();
		// Upload to the page via model
		model.addAttribute("activeUser", activeUser);
		
		return "/first";
	}
Copy the code

Five, the summary

  • Shiro user permissions come in three ways
    • programmatic
    • Annotation type
    • tabbed
  • Shiro’s REAML default is to find the configuration file information for authorization, we usually need reAML to query the corresponding information in the database. Therefore, you need to customize REAML
  • In general, the process of authentication and authorization is similar.
  • Spring is integrated with Shiro, which actually does everything through filters. Shiro provides us with many filters.
    • Configure Shiro filters in web.xml
    • Use web.xml configured filters in Shiro configuration files.
  • Configure the security manager class, configure the custom REAML, and inject the REAML into the security manager class. Transfer the security manager to Shiro’s factory for management.
  • Set static resources not to be blocked in the filter chain.
  • Shiro uses filters to authenticate users, and the flow looks like this:
    • Configure the request path for authentication
    • When accessing the programmer’s requested path, Shiro uses the FormAuthenticationFilter and calls ReAML to get the user’s information
    • Reaml can get the token, retrieve the user’s information from the database through the user name, and return null if the user does not exist
    • The FormAuthenticationFilter compares the data returned by reAML and throws an exception if it is different
    • Our request path is only used to check for exceptions thrown, not for validation.
  • Shiro also provides an interceptor for exiting the user, which we can configure with a URL.
  • When we need to obtain user data for echo, we can obtain the subject in Securityutils.getSubject (), and then obtain the identity information through the subject.

If the article has the wrong place welcome to correct, everybody exchanges with each other. Students who are used to reading technical articles on wechat and want to get more Java resources can follow the wechat public account :Java3y