Shiro address: https://shiro.apache.org/
reference resources: Crazy God says Java Springboot
1, Construction project
1.1 configuration dependency
Select the Springboot project to build, and select the web and thymeleaf templates. Spring boot starter thymeleaf, spring boot starter web and spring boot starter test will be imported automatically.
After initializing the project, import springboot to integrate with Shiro, which depends on Shiro spring.
The dependencies in pom.xml are as follows:
<dependencies> <!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-spring --> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.7.1</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
1.2 front page
Get data and page Jump through theamleaf. The project directory is as follows:
1.2.1 homepage index
src/main/resources/templates/index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>home page</h1> <p th:text="${msg}"></p> <a th:href="@{user/add}">add</a> | <a th:href="@{user/update}">update</a> </body> </html>
1.2.2 login
src/main/resources/templates/login.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>Sign in</h1> <hr> <p th:text="${msg}" style="color: red"></p> <form th:action="@{/login}"> <p>User:<input type="text" name="username"></p> <p>password:<input type="password" name="password"></p> <p><input type="submit"></p> </form> </body> </html>
1.2.3 others
All other pages are empty, with only one p tag corresponding to the file name.
2, Basic configuration
First, build the most basic configuration code.
2.1 MyController
Control page Jump
src/main/java/com/zqc/springbootshiro/controller/MyController.java
import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; @Controller public class MyController { @RequestMapping({"/","/index"}) public String toIndex(Model model) { model.addAttribute("msg", "hello,Shiro"); return "index"; } @RequestMapping("/user/add") public String add() { return "user/add"; } @RequestMapping("/user/update") public String update() { return "user/update"; } @RequestMapping("/toLogin") public String toLogin() { return "login"; } }
2.2 UserRealm
src/main/java/com/zqc/springbootshiro/config/UserRealm.java
import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection; public class UserRealm extends AuthorizingRealm { // to grant authorization @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { System.out.println("Executive authorization"); return null; } // authentication @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { System.out.println("Perform certification"); return null; } }
2.3 ShiroConfig
src/main/java/com/zqc/springbootshiro/config/ShiroConfig.java
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.HashMap; import java.util.LinkedHashMap; import java.util.Map; @Configuration public class ShiroConfig { // ShiroFilterFactoryBean @Bean public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager) { ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean(); // Set up security manager bean.setSecurityManager(defaultWebSecurityManager); return bean; } // DefaultWebSecurityManager @Bean(name = "securityManager") public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm) { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); // Associate UserRealm securityManager.setRealm(userRealm); return securityManager; } // To create a realm object, you need to customize the class @Bean(name = "userRealm") public UserRealm userRealm() { return new UserRealm(); } }
3, Login interception
3.1 modify ShiroConfig
Modify the getShiroFilterFactoryBean() method in src/main/java/com/zqc/springbootshiro/config/ShiroConfig.java to intercept login.
Create a new map to store configuration information through bean.setFilterChainDefinitionMap(filterMap); Make settings.
// ShiroFilterFactoryBean @Bean public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager) { ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean(); // Set up security manager bean.setSecurityManager(defaultWebSecurityManager); // Add shiro's built-in filter /* anon: Access without authentication authc: Authentication is required 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 */ Map<String, String> filterMap = new LinkedHashMap<>(); // The files in the user directory must be authenticated before they can be accessed. filterMap.put("/user/*", "authc"); bean.setFilterChainDefinitionMap(filterMap); // If you do not have permission, set the login request bean.setLoginUrl("/toLogin"); return bean; }
3.2 testing
Run the project and log in to the default address: http://localhost:8080/
Click add or update to jump to the login page.
4, User authentication
4.1 modifying MyController
Add a login() method in src/main/java/com/zqc/springbootshiro/controller/MyController.java.
@RequestMapping("/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, password); try { subject.login(token); // Execute the login method. If there is no exception, it indicates that it is OK return "index"; } catch (UnknownAccountException e) { // user name does not exist model.addAttribute("msg", "User name error"); return "login"; } catch (IncorrectCredentialsException e) { // Password does not exist model.addAttribute("msg", "Password error"); return "login"; } }
4.2 modifying UserRealm
Modify the UserRealm method, simulate the database to pass in the user name and password, and judge whether it is correct.
src/main/java/com/zqc/springbootshiro/config/UserRealm.java
// authentication @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { System.out.println("Perform certification"); // User name, password (normally, take it out of the database) String name = "root"; String password = "123456"; UsernamePasswordToken userToken = (UsernamePasswordToken) token; if (!userToken.getUsername().equals(name)) { return null; // Throw an exception UnknownAccountException } return new SimpleAuthenticationInfo("", password,""); }
4.3 testing
Run the project and log in to the default address: http://localhost:8080/
Click add or update to jump to the login page. Enter the account (root) and password (123456) just set
You will jump to the add or update page
5, Integrate Mybatis to complete user authentication
5.1 creating database
Create a simple database that contains the following fields and insert some data.
5.2 configuration dependency
The following configuration files are added to pom.xml to configure lombok, druid, mysql, jdbc and mybatis integration respectively.
<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.21</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jdbc</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.2.0</version> </dependency>
5.3 configuration file
Create a new src/main/resources/application.yml. It is used to configure database, druid and mybatis. The following contents need to be modified according to their own configuration:
- Database: user name, password, url
- druid: None
- mybatis: type-aliases-package,mapper-locations
spring: datasource: # 1, mysql database configuration username: root password: 123456 url: jdbc:mysql://localhost:3306/pm?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=false driver-class-name: com.mysql.cj.jdbc.Driver type: com.alibaba.druid.pool.DruidDataSource # 2, Integrated druid druid: # 1. Connection pool configuration # The number and size of connections initializing the connection pool, minimum and maximum initialSize: 5 minIdle: 5 maxActive: 20 # Configure the time to get the connection wait timeout maxWait: 60000 # Configure how often to detect idle connections that need to be closed. The unit is milliseconds timeBetweenEvictionRunsMillis: 60000 # Configure the minimum lifetime of a connection in the pool, in milliseconds minEvictableIdleTimeMillis: 300000 validationQuery: SELECT 1 FROM DUAL testWhileIdle: true testOnBorrow: false testOnReturn: false # Whether to cache the preparedStatement, that is, PSCache. It is officially recommended to close it under MySQL (personally, it is recommended to open it if you want to use SQL firewall) poolPreparedStatements: true # 2. Basic monitoring configuration web-stat-filter: enabled: true url-pattern: /* #Set which URL s are not counted exclusions: "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*" session-stat-enable: true session-stat-max-count: 100 # 3. stat-view-servlet: enabled: true url-pattern: /druid/* reset-enable: true #Set the login name and password of the monitoring page login-username: admin login-password: 123456 # Accessible allow: 127.0.0.1 # Inaccessible #deny: 192.168.1.100 # 4, Configure date format mvc: format: date: yyyy-MM-dd # 3, Integrate mybatis mybatis: # Directory of pojo type-aliases-package: com.zqc.springbootshiro.pojo # mapper directory mapper-locations: classpath:mapper/*.xml
5.4 entity USer
Note: idea requires lombok plug-in.
src/main/java/com/zqc/springbootshiro/pojo/User.java
import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Data @AllArgsConstructor @NoArgsConstructor public class User { private int id; private String name; private String pwd; }
5.5 Mapper
Write mybatis to query users:
src/main/java/com/zqc/springbootshiro/mapper/UserMapper.java
import com.zqc.springbootshiro.pojo.User; import org.apache.ibatis.annotations.Mapper; import org.springframework.stereotype.Repository; @Repository @Mapper public interface UserMapper { User queryUserByName(String name); }
src/main/resources/mapper/UserMapper.xml
<?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.zqc.springbootshiro.mapper.UserMapper"> <select id="queryUserByName" resultType="com.zqc.springbootshiro.pojo.User"> select * from mybatis.user where name=#{name} </select> </mapper>
5.6 Service
UserService interface: src/main/java/com/zqc/springbootshiro/service/UserService.java
import com.zqc.springbootshiro.pojo.User; public interface UserService { User queryUSerByName(String name); }
UserService interface implementation class: Src / main / Java / COM / zqc / springboothero / service / userserviceimpl.java
import com.zqc.springbootshiro.mapper.UserMapper; import com.zqc.springbootshiro.pojo.User; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class UserServiceImpl implements UserService { @Autowired UserMapper userMapper; @Override public User queryUSerByName(String name) { return userMapper.queryUserByName(name); } }
5.7 test database
Write test code to test whether the above functions are realized. Query the contents of the database according to the specified name.
src/test/java/com/zqc/springbootshiro/SpringbootShiroApplicationTests.java
@SpringBootTest class SpringbootShiroApplicationTests { @Autowired UserServiceImpl userService; @Test void contextLoads() { System.out.println(userService.queryUSerByName("zqc")); } }
If the query is successful, continue with the following operation.
5.8 modifying UserRealm
src/main/java/com/zqc/springbootshiro/config/UserRealm.java
Auto assemble UserServiceImpl implementation class
@Autowired UserServiceImpl userService;
Modify the doGetAuthenticationInfo() method to obtain the user name and password from the database.
UsernamePasswordToken userToken = (UsernamePasswordToken) token; // Connect to database User user = userService.queryUSerByName(userToken.getUsername()); if (user==null) { // There is no such person return null;// Throw an exception UnknownAccountException } // shiro password authentication, encrypted. return new SimpleAuthenticationInfo("", user.getPwd(),"");
The modified code is as follows:
import com.zqc.springbootshiro.pojo.User; import com.zqc.springbootshiro.service.UserServiceImpl; 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; public class UserRealm extends AuthorizingRealm { @Autowired UserServiceImpl userService; // to grant authorization @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { System.out.println("Executive authorization"); return null; } // authentication @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { System.out.println("Perform certification"); UsernamePasswordToken userToken = (UsernamePasswordToken) token; // Connect to database User user = userService.queryUSerByName(userToken.getUsername()); if (user==null) { // There is no such person return null;// Throw an exception UnknownAccountException } // shiro password authentication, encrypted. return new SimpleAuthenticationInfo("", user.getPwd(),""); }
5.9 testing
Open browser: http://localhost:8080/ Test.
Users in the database can log in.
6, Request authorization
6.1 MyController
Add an unauthorized() method in Src / main / Java / COM / zqc / springboothero / controller / mycontroller. Java.
When there is no permission limit, it displays unauthorized and inaccessible.
@RequestMapping("/noauth") @ResponseBody public String unauthorized() { return "unaccredited,cannot access"; }
6.2 modify ShiroConfig
Set the permissions of the add page.
When logging in, authentication doGetAuthenticationInfo() will be executed; When accessing a page requiring permission, authorization doGetAuthorizationInfo() will be executed, but no user is authorized at this time, so no user can access the add page.
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; @Configuration public class ShiroConfig { // ShiroFilterFactoryBean @Bean public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager) { ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean(); // Set up security manager bean.setSecurityManager(defaultWebSecurityManager); // Add shiro's built-in filter /* anon: Access without authentication authc: Authentication is required 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 */ Map<String, String> filterMap = new LinkedHashMap<>(); filterMap.put("/user/add", "perms[user:add]"); filterMap.put("/user/update", "perms[user:update]"); filterMap.put("/user/*", "authc"); bean.setFilterChainDefinitionMap(filterMap); // If you do not have permission, set the login request bean.setLoginUrl("/toLogin"); // Unauthorized page bean.setUnauthorizedUrl("/noauth"); return bean; } // DefaultWebSecurityManager @Bean(name = "securityManager") public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm) { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); // Associate UserRealm securityManager.setRealm(userRealm); return securityManager; } // To create a realm object, you need to customize the class @Bean(name = "userRealm") public UserRealm userRealm() { return new UserRealm(); } }
visit http://localhost:8080/ , visit the add page after logging in:
6.3 modifying database and entity classes
Add the perms field (varchar(100)) to set the permissions of each user. Later, the permissions of users in the database are authorized by querying their permissions.
Data in database:
At the same time, modify the User class and add the perms attribute.
import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Data @AllArgsConstructor @NoArgsConstructor public class User { private int id; private String name; private String pwd; private String perms; }
6.4 modifying UserRealm
Modify the doGetAuthenticationInfo() method:
Before modification: return new SimpleAuthenticationInfo("", user.getPwd(), "");
After modification: return new SimpleAuthenticationInfo(user, user.getPwd(), "");.
During authentication, the user object is stored in it and later taken out for use.
Modify the doGetAuthorizationInfo() method:
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); // Get the object of the currently logged in user Subject subject = SecurityUtils.getSubject(); // Remove the user stored above User currentUser = (User) subject.getPrincipal(); // Set the permissions of the current user info.addStringPermission(currentUser.getPerms());
The modified code is as follows:
import com.zqc.springbootshiro.pojo.User; import com.zqc.springbootshiro.service.UserServiceImpl; 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; public class UserRealm extends AuthorizingRealm { @Autowired UserServiceImpl userService; // to grant authorization @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { System.out.println("Executive authorization"); SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); // Get the object of the currently logged in user Subject subject = SecurityUtils.getSubject(); // Remove the user stored above User currentUser = (User) subject.getPrincipal(); // Set the permissions of the current user info.addStringPermission(currentUser.getPerms()); return info; } // authentication @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { System.out.println("Perform certification"); UsernamePasswordToken userToken = (UsernamePasswordToken) token; // Connect to database User user = userService.queryUSerByName(userToken.getUsername()); if (user==null) { // There is no such person return null;// Throw an exception UnknownAccountException } // Can be encrypted, MD5 salt value encryption // shiro password authentication, encrypted. return new SimpleAuthenticationInfo(user, user.getPwd(),""); } }
6.5 testing
Similarly, access http://localhost:8080/ After logging in, visit the add or update page. Log in to the account in the following database.
zqc users can access the add page; root user can access the update page; Other users cannot access any pages.
7, Integrate thymeleaf
7.1 dependency
<dependency> <groupId>com.github.theborakompanioni</groupId> <artifactId>thymeleaf-extras-shiro</artifactId> <version>2.0.0</version> </dependency>
7.2 modifying shiroConfig
Add the following
// Integrate ShiroDialect: used to integrate shiro and thymeleaf @Bean public ShiroDialect getShiroDialect() { return new ShiroDialect(); }
7.3 modify index.html
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro" > <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>home page</h1> <hr> <!--If the user is authenticated, the login button is not displayed--> <div shiro:notAuthenticated=""> <a th:href="@{/toLogin}">Sign in</a> </div> <p th:text="${msg}"></p> <!--If the user has add If you have no access rights, the add label--> <diV shiro:hasPermission="user:add"> <a th:href="@{user/add}">add</a> </diV> <!--If the user has update If you have no access rights, the update label--> <div shiro:hasPermission="user:update"> <a th:href="@{user/update}">update</a> </div> </body> </html>
7.4 testing
The corresponding a tag will be displayed according to the permissions of the logged in user.
zqc can view the access tag of add; root can view the access tab of update.