Shiro
1, introduction
- Apache Shiro is a Java Security (permission) framework.
- Shiro can easily develop good enough applications, which can be used not only in Java se environment, but also in Java EE environment.
- Shiro can complete authentication, authorization, encryption, session management, Web integration, caching, etc.
- Download address: https://shiro.apache.org/
2. Operation principle
Subject: the subject actually represents the User who is currently executing the operation, just because "User" generally refers to a person, but a "subject" can be a person, any third-party system, service account and any other third-party software system that is interacting with the current system.
SecurityManager: Security Manager. That is, all security related operations will interact with SecurityManager, and it manages all subject s.
Realm: there can be one or more realms, which can be considered as a secure entity data source, that is, used to obtain secure entities.
3. Shiro filter
Shiro has built-in filter, which can implement the permission related interceptor.
Common filters | Explain |
---|---|
anon | Access without authentication (login) |
authc | Must be authenticated to access |
user | If you use the function of rememberMe, you can access it directly |
perms | Have access to a resource |
roles | Have a role to access |
4. Spring boot integrates Shiro
4.1. Add dependency
<!-- mybatis --> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.2</version> </dependency> <!-- druid--> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.21</version> </dependency> <!-- mysql--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <!--jdbc --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <!-- spring-boot-starter-web --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- shiro-spring --> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.5.1</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency>
4.2 Shiro configuration class
@Configuration public class ShiroConfig { /** * Create ShiroFilterFactoryBean */ @Bean public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager securityManager){ ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); //Set up security manager shiroFilterFactoryBean.setSecurityManager(securityManager); //Add shiro built in filter /** * Shiro The built-in filter can realize the authority related amount interceptor * Common filters: * anon:Access without authentication (login) * authc:Must be authenticated to access * user:If you use the function of rememberMe, you can access it directly * perms:The resource must have resource permission to access * role:The resource must have role permission to access */ Map<String,String> filterMap = new LinkedHashMap<>(); //Authentication interception filterMap.put("/add","authc"); filterMap.put("/update","authc"); //Authorized interception filterMap.put("/add","perms[user:add]"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap); //Modify the page of the blocked request jump shiroFilterFactoryBean.setLoginUrl("/toLogin"); //Set unauthorized pages shiroFilterFactoryBean.setUnauthorizedUrl("/nuAuth"); return shiroFilterFactoryBean; } /** * Create defaultwebseriarymanager */ @Bean(name = "securityManager") public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){ DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); //Correlation realm securityManager.setRealm(userRealm); return securityManager; } /** * Create Rrealm */ @Bean(name = "userRealm") public UserRealm getRealm(){ return new UserRealm(); } }
4.3. Custom Realm
(1) AuthenticatingRealm: shiro's domain for authentication, which implements the authentication logic when users log in with the dogetauthenticationinfo method; (2) Authorizing realm: shiro is used in the field of authorization. It implements the dogetauthorizationinfo method to realize the authorization logic of users. Authorizing realm inherits authenticating realm, so it is mainly used in actual use; (3) AuthenticatingRealm and AuthorizingRealm are the two classes that provide some thread's realm interfaces in shiro (4) In the integration project with spring, shiro's SecurityManager will automatically call these two methods to realize authentication and authorization. The authentication and authorization information can be saved in the cache in combination with shiro's CacheManager.
public class UserRealm extends AuthorizingRealm { @Autowired private UserService userService; /** * Execute authorization logic * @param principalCollection * @return */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { //Empower resources SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); //Add the authorization string of the resource, which should be the same as the authorization intercepting string info.addStringPermission("user:add"); return info; } /** * Execute authentication logic * @param authenticationToken * @return * @throws AuthenticationException */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { //Write shiro judgment logic to judge user name and password UsernamePasswordToken token = (UsernamePasswordToken)authenticationToken; User user = userService.findByName(token.getUsername()); //Judge user name if(user == null){ //The user name does not exist, and shiro will throw unknown accountexception for us return null; } //To determine the password, shiro will help us determine whether the password is the same as the password in the database return new SimpleAuthenticationInfo("",user.getPassword(),""); } }
4.4 login controller
/** * Logon logic * @param username * @param password * @return */ @RequestMapping("/login") public String login(@RequestParam("username") String username, @RequestParam("password") String password, Model model){ /** * Using shiro to write authentication operations */ //Get subject Subject subject = SecurityUtils.getSubject(); //Encapsulate user data UsernamePasswordToken token = new UsernamePasswordToken(username, password); //Execute login method try { subject.login(token); //If there is no exception, login succeeded return "/test"; }catch (UnknownAccountException e){ //If there is an exception, the login fails. If an unknown accountexception occurs, the user name does not exist model.addAttribute("msg","user name does not exist"); return "/toLogin"; }catch (IncorrectCredentialsException e){ //If IncorrectCredentialsException occurs, the password is wrong model.addAttribute("msg","Password error"); return "/toLogin"; } }