Apache Shiro is a powerful and flexible open source security framework that handles security control processes common in enterprise-level applications such as authentication, authorization, session management, and encryption at a fine-grained level. The primary goal of Apache Shiro is ease of use and understanding. Sometimes security process control can be very complicated, which is a headache for developers, but it does not necessarily mean it. Frameworks should mask complexity as much as possible and expose a concise and intuitive API, thereby simplifying developers' work and ensuring their application security. This time we will talk about how to use Shiro to implement permission control in Spring Web applications.
Function
Apache Shiro is a comprehensive application security framework with many features. The following figure shows the most important functions in Shiro:
Shiro's main goals are "Four Cornerstones of Application Security" - Authentication, Authorization, Session Management and Encryption:
Architecture
From a holistic perspective, Shiro's architecture has three main concepts: Subject (subject, that is, user), Security Manager (security manager) and Realms (domain). The following figure describes the relationship between these components:
These components can be understood as follows:
Data preparation
In web applications, the main control over security is roles, resources, and permissions (what role can access what resources). A user can have multiple roles, and a role can also access multiple resources, that is, a role can correspond to multiple permissions. To implement the database design, we need to build at least 5 tables: user table, role table, resource table, role-resource table, user-role table. The structure of these 5 tables is as follows:
User table:
| id | username | password |
|---|---|---|
| 1 | Zhang San | 123456 |
| 2 | Li Si | 6666666 |
| 3 | Wang Wu | 000000 |
Role list:
| id | rolename |
|---|---|
| 1 | administrator |
| 2 | manager |
| 3 | staff |
Resource table:
| id | resname |
|---|---|
| 1 | /user/add |
| 2 | /user/delete |
| 3 | /compony/info |
Role-Resource Table:
| id | roleid | Resid |
|---|---|---|
| 1 | 1 | 1 |
| 2 | 1 | 2 |
| 3 | 2 | 3 |
User-Role Table:
| id | userid | roleid |
|---|---|---|
| 1 | 1 | 1 |
| 2 | 1 | 2 |
| 3 | 1 | 3 |
The corresponding POJO class is as follows:
/** * User*/public class User { private Integer id; private String username; private String password; //getter & setter...} /** * Role*/public class Role { private String id; private String rolename;} /** * Resource*/public class Resource { private String id; private String resname;} /** * Role-Resource*/public class RoleRes { private String id; private String roleid; private String resid;} /** * User-role*/public class UserRole { private String id; private String userid; private String roleid;} For detailed steps to integrate Spring and Shiro, please refer to my blog "Integrating Apache Shiro in Spring Applications". Here we add: You need to introduce Shiro's dependencies in advance, open mvnrepository.com, and search for Shiro. We need the first three dependencies, namely Shiro-Core, Shiro-Web and Shiro-Spring. Taking the Maven project as an example, add the following dependencies under the <dependencies> node in pom.xml :
<dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>1.4.0</version></dependency><dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-web</artifactId> <version>1.4.0</version></dependency><dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.4.0</version></dependency>
In application-context.xml you need to configure shiroFilter bean like this:
<!-- Configure shiro's filter factory class, id-shiroFilter must be consistent with the filters we configured in web.xml --><bean id="shiroFilter"> <property name="securityManager" ref="securityManager"/> <!-- Login page --> <property name="loginUrl" value="/login"/> <!-- Page after login-successful--> <property name="successUrl" value="/index"/> <!-- Illegal access to jumped pages --> <property name="unauthorizedUrl" value="/403"/> <!-- Permission configuration --> <property name="filterChainDefinitions"> <value> <!-- Static resources that can be accessed without authentication, and other urls can also be added --> /static/** = anon <!-- Except for the resources ignored above, all other resources need to be authenticated before they can be accessed --> /** = authc </value> </property></bean>
Next, you need to define Realm. Custom Realm is integrated from the AuthorizingRealm class:
public class MyRealm extends AuthorizingRealm { @Autowired private UserService userService; /** * Verification permission*/ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { String loginName = SecurityUtils.getSubject().getPrincipal().toString(); if (loginName != null) { String userId = SecurityUtils.getSubject().getSession().getAttribute("userSessionId").toString(); // Permission information object, used to store all the roles and permissions of the found user SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); // User role collection ShiroUser shiroUser = (ShiroUser) principalCollection.getPrimaryPrincipal(); info.setRoles(shiroUser.getRoles()); info.addStringPermissions(shiroUser.getUrlSet()); return info; } return null; } /** * Authentication callback function, call */ protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) { String username = (String) token.getPrincipal(); User user = new User(); sysuser.setUsername(username); try { List<SysUser> users = userService.findByNames(user); List<String> roleList= userService.selectRoleNameListByUserId(users.get(0).getId()); if (users.size() != 0) { String pwd = users.get(0).getPassword(); // After all verifications are passed, put the user information in the session Session session = SecurityUtils.getSubject().getSession(); session.setAttribute("userSession", users.get(0)); session.setAttribute("userSessionId", users.get(0).getId()); session.setAttribute("userRoles", org.apache.commons.lang.StringUtils.join(roleList,",")); return new SimpleAuthenticationInfo(username,users.get(0).getPassword()); } else { // The user was not found throw new UnknownAccountException(); } } catch (Exception e) { System.out.println(e.getMessage()); } return null; } /** * Update the user authorization information cache. */ public void clearCachedAuthorizationInfo(PrincipalCollection principals) { super.clearCachedAuthorizationInfo(principals); } /** * Update the user information cache. */ public void clearCachedAuthenticationInfo(PrincipalCollection principals) { super.clearCachedAuthenticationInfo(principals); } /** * Clear the user authorization information cache. */ public void clearAllCachedAuthorizationInfo() { getAuthorizationCache().clear(); } /** * Clear the user information cache. */ public void clearAllCachedAuthenticationInfo() { getAuthenticationCache().clear(); } /** * Clear all caches*/ public void clearCache(PrincipalCollection principals) { super.clearCache(principals); } /** * Clear all authentication caches*/ public void clearAllCache() { clearAllCachedAuthenticationInfo(); clearAllCachedAuthorizationInfo(); }}Finally, define a controller for user login to accept user login requests:
@Controllerpublic class UserController { /** * User login*/ @PostMapping("/login") public String login(@Valid User user,BindingResult bindingResult,RedirectAttributes redirectAttributes){ try { if(bindingResult.hasErrors()){ return "login"; } //Use the permission tool for authentication, after logging in successfully, jump to successUrl defined in the shiroFilter bean SecurityUtils.getSubject().login(new UsernamePasswordToken(user.getUsername(), user.getPassword())); return "redirect:index"; } catch (AuthenticationException e) { redirectAttributes.addFlashAttribute("message","error username or password"); return "redirect:login"; } } /** * Logout*/ @GetMapping("/logout") public String logout(RedirectAttributes redirectAttributes ){ SecurityUtils.getSubject().logout(); return "redirect:login"; }}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.