preface:
Hello, guys, I'm running snail rz. Of course, you can call me snail Jun. I'm a rookie who has studied Java for more than half a year. At the same time, I also have a great dream, that is, to become an excellent Java Architect one day.
This SpringBoot basic learning series is used to record the whole process of learning the basic knowledge of SpringBoot framework (this series is written with reference to the latest SpringBoot tutorial of crazy God in station B. because it was sorted out before, but it was not released at that time, there may be errors in some places. I hope you can correct them in time!)
After that, I will try to update this series at a faster rate every day. If you haven't learned SpringBoot yet, you can learn from my blog; Of course, the little friends who have studied can also review the basics with me. Finally, I hope I can make progress with you! Come on! Boys!
Due to its long length, I divide it into two blogs. The first one mainly understands Shiro's functions and the construction of the basic environment; The second part mainly studies Shiro's integration of Mybatis and Thymeleaf framework.
Special reminder: previous blog link: Integrated Shiro framework for SpringBoot basic learning (Part 1)
Today we come to the ninth stop of SpringBoot basic learning: integrating Shiro framework (Part 2). No more nonsense, let's start today's learning content!
8.4 Shiro integrates Mybatis
8.4.1 resource dependency of import module
1. Import MySQL database driver resource dependency
<!-- MySQL Database driven resource dependency --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> <version>5.1.46</version> </dependency>
2. Import mybatis to integrate springboot resource dependency
<!-- Mybatis-SpringBoot Starter for --> <!-- https://mvnrepository.com/artifact/org.mybatis.spring.boot/mybatis-spring-boot-starter --> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.3</version> </dependency>
3. Import Druid data source resource dependency
<!-- Druid Data source resource dependency --> <!-- https://mvnrepository.com/artifact/com.alibaba/druid --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.12</version> </dependency>
4. Import lombok resource dependency
<!-- lombok resources dependence --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.16.10</version> </dependency>
5. All resource dependencies
<dependencies> <!-- Shiro Three cores of: Subject: user SecurityManager: Manage all users Realm: Connection data--> <!-- springboot integration shiro Resource dependency --> <!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-spring --> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.4.1</version> </dependency> <!-- thymeleaf Resource dependency --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <!-- spring-boot-web resources dependence --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- spring-boot-test resources dependence --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!-- log4j resources dependence --> <!-- https://mvnrepository.com/artifact/log4j/log4j --> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> <!-- MySQL Database driven resource dependency --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> <version>5.1.46</version> </dependency> <!-- Starters provided by third parties --> <!-- Mybatis-SpringBoot Starter for --> <!-- https://mvnrepository.com/artifact/org.mybatis.spring.boot/mybatis-spring-boot-starter --> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.3</version> </dependency> <!-- Druid Data source resource dependency --> <!-- https://mvnrepository.com/artifact/com.alibaba/druid --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.12</version> </dependency> <!-- lombok resources dependence --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.16.10</version> </dependency> </dependencies>
8.4.2 building the basic environment of the project
1. Basic structure of project module and preparation of configuration file
1-1 basic structure of project module
1-2 write application.yml configuration file
# Set server port number server: port: 8888 # Set database driver spring: datasource: username: root password: 123456 # url connection format under version 8.0 url: jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf-8&useSSL=true # Database driver under version 8.0 driver-class-name: com.mysql.jdbc.Driver # Data source using Druid type: com.alibaba.druid.pool.DruidDataSource # Spring Boot does not inject these attribute values by default, so it needs to be configured by itself # Sets the basic configuration of the Druid data source # Number of physical connections during initialization: initialization occurs when the init method is explicitly called or when getConnection is called for the first time initialSize: 5 # Minimum number of connection pools minIdle: 5 # Maximum number of connection pools maxActive: 20 # Gets the maximum wait time for a connection, in milliseconds maxWait: 60000 # Interval between running and recycling: that is, the interval between destroying thread detection connections timeBetweenEvictionRunsMillis: 300000 # Minimum recycle time: if it is detected that the last active time and current time of the current connection are greater than the minimum recycle time, the current connection will be closed minEvictableIdleTimeMillis: 300000 # Judge whether the query statement connected to is valid validationQuery: Select 1 from dual # Detect idle connection time: it is recommended to set it to true to apply for connection detection. If the idle time is greater than the recovery run interval, execute to detect whether the connected query statement is effective testWhiteIdle: true # Check whether the connection of the imported query language is effective: this configuration will reduce performance, and the default value is false testOnBorrow: false # Check whether the effective query statement executed when returning the connection is valid: this configuration will reduce performance testOnReturn: false # Whether to cache the precompiled statement pool: that is, PSCache is used. The default value is false. There is PSCache in MySQL version 5.5 or above. It is recommended to enable it poolPreparedStatements: true # Configure filters for monitoring interception: monitoring statistics stat; Logging -log4j; Defensive SQL injection: wall # If an error occurs when running - java.lang.ClassNotFoundException: org.apache,log4j.Priority # Import log4j dependency. Maven warehouse address: https://mvnrepository.com/artifact/log4j/log4j filters: stat,log4j,wall # Sets the maximum pool precompiled statement size per connection maxPoolPreparedStatementPerConnectionSize: 20 # Set the monitoring statistics interceptor stat that uses the global data source useGlobalDataSourceStat: true # Set connection attribute: the mergesql attribute of the monitoring statistics interceptor stat is true, that is, this configuration takes effect; The slow SQL time is 0.5 seconds connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500 # Configure mybatis related properties mybatis: # Set alias by package type-aliases-package: com.kuang.pojo # Sets the location of the mapper mapping file mapper-locations: classpath:mapper/*.xml
2. Write User entity class, UserMapper interface and mapping file
2-1 writing User entity classes
package com.kuang.pojo; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import java.io.Serializable; @Data // Import parameterless constructs, set and get methods, and toString methods @AllArgsConstructor // Import parametric construction @NoArgsConstructor // The nonparametric structure is constructed again to prevent the nonparametric structure from being covered // Implement the Serializable interface to serialize the User object public class User implements Serializable { private Integer id; // number private String name; // user name private String pwd; // password }
2-2 writing UserMapper interface
package com.kuang.mapper; import com.kuang.pojo.User; import org.apache.ibatis.annotations.Mapper; import org.springframework.stereotype.Repository; @Repository // The Dao layer is managed by Spring's IOC container @Mapper // Register as Mapper interface public interface UserMapper { // Obtain the specified user information through the user name public User getUserByName(String name); }
2-3 write UserMapper.xml mapping file
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.kuang.mapper.UserMapper"> <!-- Obtain the specified user information through the user name --> <select id="getUserByName" resultType="com.kuang.pojo.User" parameterType="string"> Select id, name, pwd from mybatis.user where name = #{name} </select> </mapper>
3. Write UserService interface and UserServiceImpl implementation class
3-1 writing UserService interface
package com.kuang.service; import com.kuang.pojo.User; public interface UserService { // Obtain the specified user information through the user name public User getUserByName(String name); }
3-2 write UserServiceImpl implementation class
package com.kuang.service.impl; import com.kuang.mapper.UserMapper; import com.kuang.pojo.User; import com.kuang.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; // The Service layer is managed by Spring's IOC container @Service public class UserServiceImpl implements UserService { // Auto inject UserMapper interface @Autowired private UserMapper userMapper; // Obtain the specified user information according to the user name @Override public User getUserByName(String name) { return this.userMapper.getUserByName(name); } }
4. Modify the UserRealm configuration class and write the UserController control class
4-1 modifying UserRealm configuration class
package com.kuang.config; import com.kuang.pojo.User; import com.kuang.service.UserService; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.*; import org.apache.shiro.authc.credential.HashedCredentialsMatcher; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.authz.SimpleAuthorizationInfo; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection; import org.apache.shiro.subject.Subject; import org.apache.shiro.util.ByteSource; import org.springframework.beans.factory.annotation.Autowired; import sun.security.provider.MD5; // Customize UserRealm and inherit the AuthorizingRealm class public class UserRealm extends AuthorizingRealm { @Autowired private UserService userService; /** * 1.to grant authorization */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { System.out.println("Yes=>to grant authorization doGetAuthorizationInfo"); // 1.1 get simple authorization info object SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo(); /** * Comment out the previous add user request */ // 1.2 set the value of permission request in authorization info // authorizationInfo.addStringPermission("user:addUser"); // 1.3 get the currently logged in user (i.e. subject object) Subject subject = SecurityUtils.getSubject(); // 1.4 obtain the permission of the current user User currentUser = (User) subject.getPrincipal(); // 1.5 set the permissions of the current user and store them in the authorization info authorizationInfo.addStringPermission(currentUser.getPerms()); // 1.6 obtain the permission of the current user String perms = currentUser.getPerms(); // 1.7 judge whether the permission of the current user is empty if(perms!=null) { // 1.7.1 set the permissions of the current user and store them in the authorization info authorizationInfo.addStringPermission(currentUser.getPerms()); // 1.7.2 return the authorized authentication information return authorizationInfo; } else { // 1.7.3 if the user's permission is empty, return null value and throw an exception return null; } } /** * 2.authentication */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { System.out.println("Yes=>to grant authorization doGetAuthenticationInfo"); /** * 2.2 Connect to database */ // 2.2.1 obtain user authentication information: you can interrupt the point for testing UsernamePasswordToken userToken = (UsernamePasswordToken) authenticationToken; // 2.2.2 obtain user information through user name acquisition User user = userService.getUserByName(userToken.getUsername()); // 2.2.3 judge whether the user information is empty if(user==null) { // If the user name does not exist, an exception UnknownAccountException is thrown return null; } /** * 2.3 Password authentication * Let Shiro do it, and you can encrypt the password (in order to save trouble, we don't encrypt it here) */ return new SimpleAuthenticationInfo("",user.getPwd(),""); } }
4-2 writing UserController interface
package com.kuang.controller; import com.kuang.pojo.User; import com.kuang.service.UserService; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.*; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.authz.SimpleAuthorizationInfo; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection; import org.apache.shiro.subject.Subject; import org.springframework.beans.factory.annotation.Autowired; @Controller public class UserController { /** * Jump to home page * Use the @ RequestMapping annotation to set the request mapping path. The request method is get * Multiple requests are surrounded by {} and separated by "," */ @RequestMapping({"/","/index"}) public String toIndex(Model model) { // Set view model information model.addAttribute("msg", "Hello,Shiro!"); // Returns the logical name of the view return "index"; } /** * Jump to add page * Use the @ RequestMapping annotation to set the request mapping path. The request method is get */ @RequestMapping("/user/addUser") public String toAddPage() { // Returns the logical name of the view return "user/addUser"; } /** * Jump to the modification page * Use the @ RequestMapping annotation to set the request mapping path. The request method is get */ @RequestMapping("/user/updateUser") public String toUpdatePage() { // Returns the logical name of the view return "user/updateUser"; } /** * Jump to login page * Use the @ RequestMapping annotation to set the request mapping path. The request method is get */ @RequestMapping("/toLogin") public String toLogin() { // Returns the logical name of the view return "login"; } /** * validate logon * Use the @ PostMapping annotation to set the request mapping path. The request method is post */ @PostMapping("/login") public String login(String username, String password,Model model) { // Get current user Subject subject = SecurityUtils.getSubject(); // Encapsulate user login data UsernamePasswordToken token = new UsernamePasswordToken(username,MD5Util.MD5EncodeUtf8(password)); try{ // Execute the login method. If there is no exception, it indicates that it is OK subject.login(token); // Set the logical name of the view to index home page return "index"; // Catch unknown user exception } catch(UnknownAccountException uae) { // Set view model information model.addAttribute("msg","User name error"); // Set the logical name of the view to login page return "login"; // Catch error authentication exception } catch (IncorrectCredentialsException ica) { // Set view model information model.addAttribute("msg","Password error"); // Set the logical name of the view to login page return "login"; } } }
5. Write test classes and test results
5-1 write shirospprinbootapplicationtests test class
package com.kuang; import com.kuang.service.impl.UserServiceImpl; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest class ShiroSpringbootApplicationTests { @Autowired private UserServiceImpl userService; @Test void contextLoads() { // Print user information obtained by user name System.out.println(userService.getUserByName("root")); } }
5-2 test results
Result: the account and password information of user 6 is successfully output in the console!
8.4.3 improve the project environment
1. Modify UserRealm and ShiroConfig configuration classes
1-1 modify UserRealm configuration class
package com.kuang.config; import com.kuang.pojo.User; import com.kuang.service.UserService; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.*; import org.apache.shiro.authc.credential.HashedCredentialsMatcher; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.authz.SimpleAuthorizationInfo; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection; import org.apache.shiro.subject.Subject; import org.apache.shiro.util.ByteSource; import org.springframework.beans.factory.annotation.Autowired; import sun.security.provider.MD5; // Customize UserRealm and inherit the AuthorizingRealm class public class UserRealm extends AuthorizingRealm { // Use the @ Autowired annotation to automatically assemble the UserService interface by type @Autowired private UserService userService; /** * 1.to grant authorization */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { System.out.println("Yes=>to grant authorization doGetAuthorizationInfo"); // 1.1 get simple authorization info object SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo(); // 1.2 set the value of permission request in authorization info // authorizationInfo.addStringPermission("user:addUser"); // 1.3 get the currently logged in user (i.e. subject object) Subject subject = SecurityUtils.getSubject(); // 1.4 obtain the permission of the current user User currentUser = (User) subject.getPrincipal(); // 1.5 set the permissions of the current user and store them in the authorization info authorizationInfo.addStringPermission(currentUser.getPerms()); // 1.6 obtain the permission of the current user String perms = currentUser.getPerms(); // 1.7 judge whether the permission of the current user is empty if(perms!=null) { // 1.7.1 set the permissions of the current user and store them in the authorization info authorizationInfo.addStringPermission(currentUser.getPerms()); // 1.7.2 return the authorized authentication information return authorizationInfo; } else { // 1.7.3 if the user's permission is empty, return null value and throw an exception return null; } } /** * 2.authentication */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { System.out.println("Yes=>to grant authorization doGetAuthenticationInfo"); /** * 2.2 Connect to database */ // 2.2.1 obtain user authentication information: you can interrupt the point for testing UsernamePasswordToken userToken = (UsernamePasswordToken) authenticationToken; // 2.2.2 obtain user information through user name acquisition User user = userService.getUserByName(userToken.getUsername()); // 2.2.3 judge whether the user information is empty if(user==null) { // If the user name does not exist, an exception UnknownAccountException is thrown return null; } /** * 2.3 Password encryption and authentication */ /** * 2.3.1 You can encrypt the password: * Use MD5 encryption: e10adc3949ba59abbe56e057f20f883e * Use MD5 salt value encryption: e10adc3949ba59abbe56e057f20f883e+username */ // 2.3.2 in the principal parameter, pass in user; Password encryption uses salt value encryption return new SimpleAuthenticationInfo(user, MD5Util.MD5EncodeUtf8(user.getPwd()),""); } }
1-2 modify ShiroConfig configuration class
package com.kuang.config; import org.apache.shiro.spring.web.ShiroFilterFactoryBean; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.LinkedHashMap; import java.util.Map; // Use the @ Configuration annotation to make ShiroConfig a Configuration class @Configuration public class ShiroConfig { /** * 1.To create a realm object, you need to customize the class */ // 1.1 register userRealm as a component in the IOC container of Spring @Bean public UserRealm userRealm() { // 1.2 the return value is to create a UserRealm object return new UserRealm(); } /** * 2.Set defaultwebsecuritymanager (default Web security manager) */ // 2.1 use the @ Bean annotation to register the getDefaultWebSecurityManager method as a component in the IOC container of Spring @Bean(name="securityManager") // 2.2 use the @ Qualifier annotation to obtain the userrealm object in the SpringdeIOC container through the name userrealm public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm) { // 2.3 get DefaultWebSecurityManager object DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); // 2.4 associating userRealm objects securityManager.setRealm(userRealm); // 2.5 return securityManager object return securityManager; } /** * 3.Set ShiroFilterFactoryBean(Shiro filter factory Bean) */ // Use the @ Bean annotation to register the getShiroFilterFactoryBean method into the Spring container @Bean // Use the @ Qualifier annotation to get the DefaultWebSecurityManager object in the Spring container by the name securityManage public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager securityManager) { // 3.1 obtain Shiro's filter factory and set up the security manager // 3.1.1 get ShiroFilterFactoryBean object ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean(); // 3.1.2 setting the securitymanager object factoryBean.setSecurityManager(securityManager); /** * 3.2 Add shiro's built-in filter * anon: Access without authentication * authc: Must be authenticated to access * user: You must have the remember me function to use * perms: You must have permission on a resource to access it * role: You must have a role permission to access */ /** * 3.2.1 intercept */ // Get LinkedHashMap object Map<String, String> filterMap = new LinkedHashMap<>(); /** * 3.2.2 to grant authorization * Use the Map set to set the key value value corresponding to the permission: key is the corresponding request and value is the permission value */ // #1 "/user/addUser" is a request to add a user, and "perms[user:addUser]" means that you can only access it if you have the permission to add a user filterMap.put("/user/addUser", "perms[user:addUser]"); // #2 "/user/updateUser" is a request to modify a user, "perms[user:updateUser]" means that only users with modify user permission can access it filterMap.put("/user/updateUser", "perms[user:updateUser]"); // #3 "/user / *" for all requests under user, "authc" means that they can only be accessed after authentication filterMap.put("/user/*", "authc"); // 3.2.3 Set filterchaindefinitionmap factoryBean.setFilterChainDefinitionMap(filterMap); /** * 3.3 Set login and unauthorized related requests */ // 3.3.1 setting login request factoryBean.setLoginUrl("/toLogin"); // 3.3.2 request for unauthorized page factoryBean.setUnauthorizedUrl("/unAuthorized"); // 3.4 return factoryBean object return factoryBean; } }
2. Modify UserController control class
package com.kuang.controller; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.IncorrectCredentialsException; import org.apache.shiro.authc.UnknownAccountException; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.subject.Subject; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.ResponseBody; @Controller public class UserController { /** * Jump to home page * Use the @ RequestMapping annotation to set the request mapping path. The request method is get * Multiple requests are surrounded by {} and separated by "," */ @RequestMapping({"/","/index"}) public String toIndex(Model model) { // Set view model information model.addAttribute("msg", "Hello,Shiro!"); // Returns the logical name of the view return "index"; } /** * Jump to add page * Use the @ RequestMapping annotation to set the request mapping path. The request method is get */ @RequestMapping("/user/addUser") public String toAddPage() { // Returns the logical name of the view return "user/addUser"; } /** * Jump to the modification page * Use the @ RequestMapping annotation to set the request mapping path. The request method is get */ @RequestMapping("/user/updateUser") public String toUpdatePage() { // Returns the logical name of the view return "user/updateUser"; } /** * Jump to login page * Use the @ RequestMapping annotation to set the request mapping path. The request method is get */ @RequestMapping("/toLogin") public String toLogin() { // Returns the logical name of the view return "login"; } /** * validate logon * Use the @ PostMapping annotation to set the request mapping path. The request method is post */ @PostMapping("/login") public String login(String username, String password,Model model) { // Get current user Subject subject = SecurityUtils.getSubject(); // Encapsulate user login data UsernamePasswordToken token = new UsernamePasswordToken(username,MD5Util.MD5EncodeUtf8(password)); try{ // Execute the login method. If there is no exception, it indicates that it is OK subject.login(token); // Set the logical name of the view to index home page return "index"; // Catch unknown user exception } catch(UnknownAccountException uae) { // Set view model information model.addAttribute("msg","User name error"); // Set the logical name of the view to login page return "login"; // Catch error authentication exception } catch (IncorrectCredentialsException ica) { // Set view model information model.addAttribute("msg","Password error"); // Set the logical name of the view to login page return "login"; } } /** * Unauthorized * Use the @ RequestMapping annotation to set the request mapping path. The request method is get */ @RequestMapping("/unAuthorized") // Use the @ ResponseBody annotation to convert the string returned by the method into a JSON string @ResponseBody public String unAuthorized() { return "Unauthorized, unable to access this page!"; } }
3. Modify User entity class and write MD5 tool class
3-1 modifying User entity class
package com.kuang.pojo; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import java.io.Serializable; @Data // Import parameterless constructs, set and get methods, and toString methods @AllArgsConstructor // Import parametric construction @NoArgsConstructor // The nonparametric structure is constructed again to prevent the nonparametric structure from being covered // Implement the Serializable interface to serialize the User object public class User implements Serializable { private Integer id; //number private String name; //user name private String pwd; //password private String perms; //jurisdiction }
3-2 writing MD5Util tool class
package com.kuang.utils; import java.security.MessageDigest; public class MD5Util { private static String byteArrayToHexString(byte b[]) { StringBuffer resultSb = new StringBuffer(); for (int i = 0; i < b.length; i++) resultSb.append(byteToHexString(b[i])); return resultSb.toString(); } private static String byteToHexString(byte b) { int n = b; if (n < 0) n += 256; int d1 = n / 16; int d2 = n % 16; return hexDigits[d1] + hexDigits[d2]; } /** * Return uppercase MD5 * * @param origin Initial value * @param charsetname Character set name * @return String Salt value encrypted string */ private static String MD5Encode(String origin, String charsetname) { String resultString = null; try { resultString = new String(origin); MessageDigest md = MessageDigest.getInstance("MD5"); if (charsetname == null || "".equals(charsetname)) resultString = byteArrayToHexString(md.digest(resultString.getBytes())); else resultString = byteArrayToHexString(md.digest(resultString.getBytes(charsetname))); } catch (Exception exception) { } return resultString.toUpperCase(); } public static String MD5EncodeUtf8(String origin) { //Salt value salt encryption //origin = origin + PropertiesUtil.getProperty("password.salt", ""); return MD5Encode(origin, "utf-8"); } private static final String hexDigits[] = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"}; }
4. Modify database information
4-1 modify User table
- Add the perms field of varchar type to set the user's permissions
4-2 adding permissions to users
4. Authority verification test
4-1 log in with root account
- Log in as root to test
- Access the add user page
- Access the modify user page
4-2 login with admin account
- Log in to the admin user to test
- Visit the new user page
- Access the modify user page
8.5 Shiro integrates thymeleaf
8.5.1 import resource dependency and modify some codes
1. Import shiro to integrate thymeleaf resource dependency
<!-- shiro-thymeleaf Integration package --> <!-- https://mvnrepository.com/artifact/com.github.theborakompanioni/thymeleaf-extras-shiro --> <dependency> <groupId>com.github.theborakompanioni</groupId> <artifactId>thymeleaf-extras-shiro</artifactId> <version>2.0.0</version> </dependency>
2. Modify ShiroConfig configuration class
package com.kuang.config; import at.pollux.thymeleaf.shiro.dialect.ShiroDialect; import org.apache.shiro.spring.web.ShiroFilterFactoryBean; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.LinkedHashMap; import java.util.Map; // Use the @ Configuration annotation to make ShiroConfig a Configuration class @Configuration public class ShiroConfig { /** * 1.To create a realm object, you need to customize the class */ // 1.1 register userRealm as a component in the IOC container of Spring @Bean public UserRealm userRealm() { // 1.2 the return value is to create a UserRealm object return new UserRealm(); } /** * 2.Set defaultwebsecuritymanager (default Web security manager) */ // 2.1 use the @ Bean annotation to register the getDefaultWebSecurityManager method as a component in the IOC container of Spring @Bean(name="securityManager") // 2.2 use the @ Qualifier annotation to obtain the userrealm object in the SpringdeIOC container through the name userrealm public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm) { // 2.3 get DefaultWebSecurityManager object DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); // 2.4 associating userRealm objects securityManager.setRealm(userRealm); // 2.5 return securityManager object return securityManager; } /** * 3.Set ShiroFilterFactoryBean(Shiro filter factory Bean) */ // Use the @ Bean annotation to register the getShiroFilterFactoryBean method into the Spring container @Bean // Use the @ Qualifier annotation to get the DefaultWebSecurityManager object in the Spring container by the name securityManage public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager securityManager) { // 3.1 obtain Shiro's filter factory and set up the security manager // 3.1.1 get ShiroFilterFactoryBean object ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean(); // 3.1.2 setting the securitymanager object factoryBean.setSecurityManager(securityManager); /** * 3.2 Add shiro's built-in filter * anon: Access without authentication * authc: Must be authenticated to access * user: You must have the remember me function to use * perms: You must have permission on a resource to access it * role: You must have a role permission to access */ /** * 3.2.1 intercept */ // Get LinkedHashMap object Map<String, String> filterMap = new LinkedHashMap<>(); /** * 3.2.2 to grant authorization * Use the Map set to set the key value value corresponding to the permission: key is the corresponding request and value is the permission value */ // #1 "/user/addUser" is a request to add a user, and "perms[user:addUser]" means that you can only access it if you have the permission to add a user filterMap.put("/user/addUser", "perms[user:addUser]"); // #2 "/user/updateUser" is a request to modify a user, "perms[user:updateUser]" means that only users with modify user permission can access it filterMap.put("/user/updateUser", "perms[user:updateUser]"); // #3 "/user / *" for all requests under user, "authc" means that they can only be accessed after authentication filterMap.put("/user/*", "authc"); // 3.2.3 Set filterchaindefinitionmap factoryBean.setFilterChainDefinitionMap(filterMap); /** * 3.3 Set login and unauthorized related requests */ // 3.3.1 setting login request factoryBean.setLoginUrl("/toLogin"); // 3.3.2 request for unauthorized page factoryBean.setUnauthorizedUrl("/unAuthorized"); // 3.4 return factoryBean object return factoryBean; } /** * 4.Integrate ShiroDialect(Shiro dialect): used to integrate shiro thymeleaf */ // 4.1 register ShiroDialect into the Spring container @Bean public ShiroDialect getShiroDialect() { // 4.2 return ShiroDialect object return new ShiroDialect(); } }
3. Modify UserRealm configuration class
package com.kuang.config; import com.kuang.pojo.User; import com.kuang.service.UserService; import com.kuang.utils.MD5Util; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.*; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.authz.SimpleAuthorizationInfo; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.session.Session; import org.apache.shiro.subject.PrincipalCollection; import org.apache.shiro.subject.Subject; import org.springframework.beans.factory.annotation.Autowired; // Customize UserRealm and inherit the AuthorizingRealm class public class UserRealm extends AuthorizingRealm { @Autowired private UserService userService; /** * 1.to grant authorization */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { System.out.println("Yes=>to grant authorization doGetAuthorizationInfo"); // 1.1 get simple authorization info object SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo(); // 1.3 get the currently logged in user (i.e. subject object) Subject subject = SecurityUtils.getSubject(); // 1.4 obtain the permission of the current user User currentUser = (User) subject.getPrincipal(); // 1.5 set the permissions of the current user and store them in the authorization info authorizationInfo.addStringPermission(currentUser.getPerms()); // 1.6 obtain the permission of the current user String perms = currentUser.getPerms(); // 1.7 judge whether the permission of the current user is empty if(perms!=null) { // 1.7.1 set the permissions of the current user and store them in the authorization info authorizationInfo.addStringPermission(currentUser.getPerms()); // 1.7.2 return the authorized authentication information return authorizationInfo; } else { // 1.7.3 if the user's permission is empty, return null value and throw an exception return null; } } /** * 2.authentication */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { System.out.println("Yes=>to grant authorization doGetAuthenticationInfo"); /** * 2.2 Connect to database */ // 2.2.1 obtain user authentication information: you can interrupt the point for testing UsernamePasswordToken userToken = (UsernamePasswordToken) authenticationToken; // 2.2.2 obtain user information through user name acquisition User user = userService.getUserByName(userToken.getUsername()); // 2.2.3 judge whether the user information is empty if(user==null) { // If the user name does not exist, an exception UnknownAccountException is thrown return null; } /** * 3.Get user session information */ // 3.1 get current user Subject currentUser = SecurityUtils.getSubject(); // 3.2 get the session information of the current user Session userSession = currentUser.getSession(); // 3.3 set the session information of the current user and encapsulate the user name into loginUser userSession.setAttribute("loginUser",user.getName()); /** * 2.4 Password encryption and authentication */ /** * 2.4.1 You can encrypt the password: * Use MD5 encryption: e10adc3949ba59abbe56e057f20f883e * Use MD5 salt value encryption: e10adc3949ba59abbe56e057f20f883e+username */ // 2.4.2 in the principal parameter, pass in the user; Password encryption uses salt value encryption return new SimpleAuthenticationInfo(user, user.getPwd(),""); } }
4. Modify UserController control class
package com.kuang.controller; import com.kuang.utils.MD5Util; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.IncorrectCredentialsException; import org.apache.shiro.authc.UnknownAccountException; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.subject.Subject; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; @Controller public class UserController { /** * Jump to home page * Use the @ RequestMapping annotation to set the request mapping path. The request method is get * Multiple requests are surrounded by {} and separated by "," */ @RequestMapping({"/","/index"}) public String toIndex(Model model) { // Set view model information model.addAttribute("msg", "Hello,Shiro!"); // Returns the logical name of the view return "index"; } /** * Jump to add page * Use the @ RequestMapping annotation to set the request mapping path. The request method is get */ @RequestMapping("/user/addUser") public String toAddPage() { // Returns the logical name of the view return "user/addUser"; } /** * Jump to the modification page * Use the @ RequestMapping annotation to set the request mapping path. The request method is get */ @RequestMapping("/user/updateUser") public String toUpdatePage() { // Returns the logical name of the view return "user/updateUser"; } /** * Jump to login page * Use the @ RequestMapping annotation to set the request mapping path. The request method is get */ @RequestMapping("/toLogin") public String toLogin() { // Returns the logical name of the view return "login"; } /** * validate logon * Use the @ PostMapping annotation to set the request mapping path. The request method is post */ @PostMapping("/login") public String login(String username, String password,Model model) { // Get current user Subject subject = SecurityUtils.getSubject(); // Encapsulate user login data UsernamePasswordToken token = new UsernamePasswordToken(username,MD5Util.MD5EncodeUtf8(password)); try{ // Execute the login method. If there is no exception, it indicates that it is OK subject.login(token); // Set the logical name of the view to index home page return "index"; // Catch unknown user exception } catch(UnknownAccountException uae) { // Set view model information model.addAttribute("msg","User name error"); // Set the logical name of the view to login page return "login"; // Catch error authentication exception } catch (IncorrectCredentialsException ica) { // Set view model information model.addAttribute("msg","Password error"); // Set the logical name of the view to login page return "login"; } } /** * Unauthorized * Use the @ RequestMapping annotation to set the request mapping path. The request method is get */ @RequestMapping("/unAuthorized") // Use the @ ResponseBody annotation to convert the string returned by the method into a JSON string @ResponseBody public String unAuthorized() { return "Unauthorized, unable to access this page!"; } /** * Log out * Use the @ RequestMapping annotation to set the request mapping path. The request method is get */ @RequestMapping("/logout") public String logout(){ // Get current user information Subject currentUser = SecurityUtils.getSubject(); // Log off the currently logged in user currentUser.logout(); // Return to the index home page return "index"; } }
8.5.2 page modification and login verification test
1. Modify login.html landing page
<!DOCTYPE html> <!-- introduce thymeleaf Namespace for --> <html lang="en" xmlns:th=http://www.thymeleaf.org> <head> <meta charset="UTF-8"> <title>Sign in</title> </head> <body> <h1>Sign in</h1> <hr/> <p th:text="${msg}" style="color: red"></p> <!-- Login verification form --> <form th:action="@{/login}" method="post"> <p> user name:<input type="text" name="username"/> </p> <p> password:<input type="password" name="password"/> </p> <p> <input type="checkbox"/>Remember me </p> <input type="submit" value="Sign in" /> </form> </body> </html>
2. Modify the index.html home page
<!DOCTYPE html> <!-- introduce thymeleaf and thymeleaf integration shiro Namespace for --> <html lang="en" xmlns:th=http://www.thymeleaf.org xmlns:shiro="http://www.pollix.at/thymeleaf/shiro"> <head> <meta charset="UTF-8"> <title>home page</title> </head> <body> <h1>home page</h1> <!-- 1.Login account- -> <!-- 1.1 from session Medium judgment value --> <!-- <div th:if="${session.loginUser==null}"> <a th:href="@{/toLogin}">Sign in</a> </div> --> <!-- 1.2 Determine whether it is authenticated --> <!-- <div shiro:notAuthenticated=""> <a th:href="@{/toLogin}">Sign in</a> </div>--> <!-- 1.3 Judge whether it is a tourist --> <!-- <div shiro:guest="true"> <a th:href="@{/toLogin}">Sign in</a> </div> --> <!-- 1.4 judge session Empty: use ternary operator --> <div> <!--If the current user is empty, login will be displayed; If it is not empty, the user's will be displayed session information--> <a th:href="@{/toLogin}" th:text="${session.loginUser==null?'Sign in':session.loginUser}"></a> </div> <!-- 2.Cancellation of account --> <!-- 2.1 Judge whether the user is not empty --> <div th:if="${session.loginUser!=null}"> <!-- 2.2 If it is not empty, it is allowed to log off --> <a th:href="@{/logout}">cancellation</a> </div> <!-- use th:text To display information --> <p th:text="${msg}"></p> <hr/> <!-- 3.Set the entry for adding and modifying users --> <!-- 3.1 have"user:addUser"Permission to see --> <div shiro:haspermission="'user:addUser'"> <a th:href="@{/user/addUser}">Add user</a> </div> <!-- 3.2 have"user:updateUser"Permission to see --> <div shiro:haspermission="'user:updateUser'"> <a th:href="@{/user/updateUser}">Modify user</a> </div> </body> </html>
3. Login verification test
3-1 log in with root account
- Before logging in
- Log in to the root account
- Visit home page successfully
- Access the modify user page
- Log out
3-2 login with admin account
- Log in to the root account
- Visit home page successfully
- Visit the new user page
Well, today's study on the Shiro framework for the integration of SpringBoot basic learning (Part 2) is over. Welcome to actively study and discuss with your friends. You can pay attention to snail Jun if you like. By the way, let's have a one click triple connection. See you next time. Bye!
Reference video link: https://www.bilibili.com/video/BV1PE411i7CV([crazy God says Java] the latest tutorial of SpringBoot, the IDEA version is easy to understand)