Spring Boot (14) -- Integrated Shiro
23. Introduction to Shiro
23.1 what is Shiro
- Apache Shiro is a security (permission) framework for Java
- 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, integration with the Web, caching, etc
- Download address
- Official website: Apache Shiro | Simple. Java. Security.
- github: Apache Shiro | Simple. Java. Security.
23.2 what functions are included
- Authentication: identity authentication / login to verify whether the user has the corresponding identity
- Authorization: authorization, i.e. permission verification, which verifies whether an authenticated user has a certain permission; That is, judge whether the user can perform any operation, such as verifying whether a user has a role. Or fine-grained verification of whether a user has certain permissions on a resource
- Session Management: Session Management, that is, after a user logs in, it is a session. Before exiting, all its information is in the session; The session can be a normal Java se environment or a Web environment
- Cryptography: encryption to protect the security of data. For example, the password is encrypted and stored in the database instead of plaintext
- Web Support: Web Support can be easily integrated into the web environment
- Caching: caching. For example, after a user logs in, it is not necessary to check his / her user information and roles / permissions every time, which can improve efficiency
- Concurrency: Shiro supports concurrent verification of multithreaded applications, that is, if you start another thread in one thread, you can automatically propagate permissions in the past
- Testing: provides test support
- "Run As": allow one user to pretend to be the identity of another user (if they allow it)
- Remember Me: Remember Me, this is a very common function, that is, after logging in once, you don't need to log in next time
23.3 Shiro external architecture
- Subject: the object that the application code directly interacts with is subject, that is to say, the core of Shiro's external API is subject. Subject represents the current "user". This user is not necessarily a specific person. Anything interacting with the current application is a subject, such as web crawler, robot, etc; All interactions with the subject will be delegated to the SecurityManager; Subject is actually a facade, and SecurityManager is the actual executor
- SecurityManager: Security Manager; That is, all security related operations will interact with SecurityManager; And it manages all subjects; It can be seen that it is the core of Shiro. It is responsible for interacting with other components of Shiro. It is equivalent to the role of dispatcher servlet in spring MVC
- Real: Shiro obtains security data (such as users, roles and permissions) from real, which means that if SecurityManager wants to verify user identity, it needs to obtain corresponding users from real for comparison to determine whether the user identity is legal; You also need to get the corresponding role / authority of the user from Realm to verify whether the user can operate; You can think of Realm as a DataSource
23.4 internal structure of Shiro
- Subject: any "user" who can interact with the application;
- SecurityManager: equivalent to dispatcher servlet in spring MVC; It's Shiro's heart; All specific interactions are controlled through the SecurityManager; It manages all subjects and is responsible for authentication, authorization, session and cache management.
- Authenticator: responsible for Subject authentication. It is an extension point and can be customized; Authentication Strategy can be used, that is, when the user has passed the authentication;
- Authorizer: authorizer, that is, access controller, which is used to determine whether the subject has permission to perform corresponding operations; That is, it controls which functions users can access in the application;
- Realm: there can be one or more realms, which can be considered as the data source of security entity, that is, the data source used to obtain security entity; It can be implemented by JDBC, memory, etc; Provided by the user; Therefore, in general, you need to implement your own realm in the application;
- SessionManager: the component that manages the Session life cycle; Shiro can be used not only in the Web environment, but also in the ordinary Java se environment
- CacheManager: cache controller to manage the cache of users, roles, permissions, etc; Because these data are rarely changed, putting them in the cache can improve the performance of access
- Cryptography: password module. Shiro improves some common encryption components, such as password encryption / decryption.
23.5 Shiro project analysis
-
Rapid practice
-
View official documents: Apache Shiro Tutorial | Apache Shiro
-
Official quickstart: github.com
-
Create a maven parent project, delete unnecessary parts, and copy the following contents on the official website
-
Import Shiro dependencies
<dependencies> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>1.4.1</version> </dependency> <!-- configure logging --> <!-- https://mvnrepository.com/artifact/org.slf4j/jcl-over-slf4j --> <dependency> <groupId>org.slf4j</groupId> <artifactId>jcl-over-slf4j</artifactId> <version>1.7.21</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.21</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> </dependencies>
- log4j.properties
log4j.rootLogger=INFO, stdout log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m %n # General Apache libraries log4j.logger.org.apache=WARN # Spring log4j.logger.org.springframework=WARN # Default Shiro logging log4j.logger.org.apache.shiro=INFO # Disable verbose logging log4j.logger.org.apache.shiro.util.ThreadContext=WARN log4j.logger.org.apache.shiro.cache.ehcache.EhCache=WARN
- shiro.ini, you need to install an ini file plug-in in the IDEA to highlight it
[users] # user 'root' with password 'secret' and the 'admin' role root = secret, admin # user 'guest' with the password 'guest' and the 'guest' role guest = guest, guest # user 'presidentskroob' with password '12345' ("That's the same combination on # my luggage!!!" ;)), and role 'president' presidentskroob = 12345, president # user 'darkhelmet' with password 'ludicrousspeed' and roles 'darklord' and 'schwartz' darkhelmet = ludicrousspeed, darklord, schwartz # user 'lonestarr' with password 'vespa' and roles 'goodguy' and 'schwartz' lonestarr = vespa, goodguy, schwartz # ----------------------------------------------------------------------------- # Roles with assigned permissions # # Each line conforms to the format defined in the # org.apache.shiro.realm.text.TextConfigurationRealm#setRoleDefinitions JavaDoc # ----------------------------------------------------------------------------- [roles] # 'admin' role has all permissions, indicated by the wildcard '*' admin = * # The 'schwartz' role can do anything (*) with any lightsaber: schwartz = lightsaber:* # The 'goodguy' role is allowed to 'drive' (action) the winnebago (type) with # license plate 'eagle5' (instance specific id) goodguy = winnebago:drive:eagle5
- Start class Quickstart
/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.*; import org.apache.shiro.config.IniSecurityManagerFactory; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.session.Session; import org.apache.shiro.subject.Subject; import org.apache.shiro.util.Factory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Simple Quickstart application showing how to use Shiro's API. * Easy to get started Shiro uses the API * * @since 0.9 RC2 */ public class Quickstart { private static final transient Logger log = LoggerFactory.getLogger(Quickstart.class); public static void main(String[] args) { // The easiest way to create a Shiro SecurityManager with configured // realms, users, roles and permissions is to use the simple INI config. // We'll do that by using a factory that can ingest a .ini file and // return a SecurityManager instance: // Use the shiro.ini file at the root of the classpath // (file: and url: prefixes load from files and urls respectively): // Read configuration file: Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini"); SecurityManager securityManager = factory.getInstance(); // for this simple example quickstart, make the SecurityManager // accessible as a JVM singleton. Most applications wouldn't do this // and instead rely on their container configuration or web.xml for // webapps. That is outside the scope of this simple quickstart, so // we'll just do the bare minimum so you can continue to get a feel // for things. SecurityUtils.setSecurityManager(securityManager); // Now that a simple Shiro environment is set up, let's see what you can do: // get the currently executing user: // Get the current user object Subject Subject currentUser = SecurityUtils.getSubject(); // Do some stuff with a Session (no need for a web or EJB container!!!) //The Session of Shiro obtained by the current user can be separated from the web stored value Session session = currentUser.getSession(); session.setAttribute("someKey", "aValue"); String value = (String) session.getAttribute("someKey"); if (value.equals("aValue")) { log.info("Retrieved the correct value! [" + value + "]"); } // let's login the current user so we can check against roles and permissions: //Judge whether the current user is authenticated if (!currentUser.isAuthenticated()) { //Token token UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa"); //Set remember me token.setRememberMe(true); try { //Perform login operation currentUser.login(token); } catch (UnknownAccountException uae) { log.info("There is no user with username of " + token.getPrincipal()); } catch (IncorrectCredentialsException ice) { log.info("Password for account " + token.getPrincipal() + " was incorrect!"); } catch (LockedAccountException lae) { log.info("The account for username " + token.getPrincipal() + " is locked. " + "Please contact your administrator to unlock it."); } // ... catch more exceptions here (maybe custom ones specific to your application? catch (AuthenticationException ae) { //unexpected condition? error? } } //say who they are: //print their identifying principal (in this case, a username): log.info("User [" + currentUser.getPrincipal() + "] logged in successfully."); //test a role: // Check role if (currentUser.hasRole("schwartz")) { log.info("May the Schwartz be with you!"); } else { log.info("Hello, mere mortal."); } //test a typed permission (not instance-level) //Coarse grain size if (currentUser.isPermitted("lightsaber:wield")) { log.info("You may use a lightsaber ring. Use it wisely."); } else { log.info("Sorry, lightsaber rings are for schwartz masters only."); } //a (very powerful) Instance Level permission: //Fine grained if (currentUser.isPermitted("winnebago:drive:eagle5")) { log.info("You are permitted to 'drive' the winnebago with license plate (id) 'eagle5'. " + "Here are the keys - have fun!"); } else { log.info("Sorry, you aren't allowed to drive the 'eagle5' winnebago!"); } //all done - log out! //cancellation currentUser.logout(); //end System.exit(0); } }
- The basic functions are Spring Security, but the name is changed in Shiro
// Get the current user object Subject Subject currentUser = SecurityUtils.getSubject(); //The Session of Shiro obtained by the current user can be separated from the web stored value Session session = currentUser.getSession(); //Judge whether the current user is authenticated currentUser.isAuthenticated() //Obtain the authentication of the current user currentUser.getPrincipal() //Get role permissions currentUser.hasRole("schwartz") //Coarse grained permissions currentUser.isPermitted("lightsaber:wield") //cancellation currentUser.logout();
24. SpringBoot integrated Shiro
24.1 environmental construction
- Create a new module, select the springboot project, and import a web and thymeleaf dependency
- pom.xml
<dependencies> <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>
- Create a new MyController and test whether the running environment is normal
@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"; } }
- New index HTML page
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>home page</h1> <p th:text="${msg}"></p> <hr> <a th:href="@{/user/add}">add</a> | <a th:href="@{/user/update}">update</a> </body> </html>
- Create a new add. In the user folder HTML page
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>add</h1> </body> </html>
- Create a new update in the user folder HTML page
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>update</h1> </body> </html>
- Project structure
- Importing shiro to integrate spring dependencies
<!--shiro integration spring My bag--> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.6.0</version> </dependency>
- Write custom class userrealm
//Custom realm public class UserRealm extends AuthorizingRealm { //to grant authorization @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { System.out.println("Authorization executed"); return null; } //authentication @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { System.out.println("Certification performed"); return null; } }
- Write configuration class ShiroConfig
@Configuration public class ShiroConfig { //ShiroFilterFactoryBean,DefaultWebSecurityManager,realm //Step 1: create a realm object @Bean public UserRealm userRealm(){ return new UserRealm(); } //Step 2: DefaultWebSecurityManager @Bean public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){ DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); //Associate UserRealm securityManager.setRealm(userRealm); return securityManager; } //Step 3: ShiroFilterFactoryBean @Bean public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("getDefaultWebSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager){ ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean(); //Set up security manager bean.setSecurityManager(defaultWebSecurityManager); return bean; } }
- Test. At this time, add and update can jump freely
24.2 Shiro realizes login interception
-
Add the following configuration to the getShiroFilterFactoryBean() method of ShiroConfig. The meaning of the second parameter is as follows
-
anon: access without authentication
-
authc: must be authenticated to access
-
user: you must have the remember me function to use
-
perms: you can only access a resource with permission
-
Role: having a role permission
-
@Bean public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("getDefaultWebSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager){ ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean(); //Set up security manager bean.setSecurityManager(defaultWebSecurityManager); //Add shiro's built-in filter Map<String,String> filterMap = new LinkedHashMap<>(); filterMap.put("/user/*","authc"); // filterMap.put("/user/add","anon"); // filterMap.put("/user/update","authc"); bean.setFilterChainDefinitionMap(filterMap); return bean; }
- At this time, click add or update, and the page Jump fails
- After successful interception, jump to the login page and write login html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Login page</title> </head> <body> <h1>Sign in</h1> <hr> <form action=""> <p>user name:<input type="text" name="username"></p> <p>password:<input type="text" name="password"></p> <p>password:<input type="submit"></p> </form> </body> </html>
- Add the toLogin() method in MyController
@Controller public class MyController { ... @RequestMapping("/toLogin") public String toLogin(){ return "login"; } }
- Add the login page Jump configuration in the getShiroFilterFactoryBean() method of ShiroConfig
@Bean public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("getDefaultWebSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager){ ... //If you do not have permission, jump to the login page bean.setLoginUrl("/toLogin"); return bean; }
- Test to realize the function of jumping to the login page after interception
24.3 Shiro realizes user authentication
- Write the processing method after the user submits the form in MyController login()
@RequestMapping("/login") public String login(String username, String password, Model model) { //Get a user Subject subject = SecurityUtils.getSubject(); // Encapsulated user login data UsernamePasswordToken token = new UsernamePasswordToken(username, password); try { //Execute the login method. If there is no exception, it indicates that the login is successful subject.login(token); 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"; } }
- Modify login HTML page
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>Login page</title> </head> <body> <h1>Sign in</h1> <hr> <p th:text="${msg}" style="color: red;"></p> <form th:action="@{/login}"> <p>user name:<input type="text" name="username"></p> <p>password:<input type="text" name="password"></p> <input type="submit"> </form> </body> </html>
- During the test run, it is found that the doGetAuthenticationInfo() authentication method in UserRealm is running
- Write the authentication in UserRealm in user authentication
//authentication @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { System.out.println("Certification performed"); //Forge user name and password String name = "zzz"; String password = "123456"; //Judge user name and password //Token is the user data token encapsulated in MyController UsernamePasswordToken userToken = (UsernamePasswordToken) token; //User name authentication if(!userToken.getUsername().equals(name)){ //Throw an exception UnknownAccountException return null; } //Password authentication Shiro do, do not write their own //The first parameter: obtain the authentication of the current user; The second parameter: the password to be passed; The third parameter: authentication name return new SimpleAuthenticationInfo("",password,""); }
24.4 Shiro integrates MyBatis
- Import dependency
<dependencies> <!--mysql--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <!--druid--> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.2.1</version> </dependency> <!--log4j--> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> <!--introduce mybatis,This is MyBatis Officially provided adaptations spring Boot Yes, not spring Boot own--> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.3</version> </dependency> <!--thymeleaf--> <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> <!--shiro integration spring My bag--> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.6.0</version> </dependency> </dependencies>
- Configuration file application Compilation of yaml
spring: datasource: username: root password: 123456 #? serverTimezone=UTC resolves the error in the time zone url: jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8 driver-class-name: com.mysql.cj.jdbc.Driver type: com.alibaba.druid.pool.DruidDataSource #Spring Boot does not inject these attribute values by default and needs to bind by itself #druid data source proprietary configuration initialSize: 5 minIdle: 5 maxActive: 20 maxWait: 60000 timeBetweenEvictionRunsMillis: 60000 minEvictableIdleTimeMillis: 300000 validationQuery: SELECT 1 FROM DUAL testWhileIdle: true testOnBorrow: false testOnReturn: false poolPreparedStatements: true #Configure filters for monitoring statistics interception, stat: monitoring statistics, log4j: logging, wall: defensive sql injection #If allowed, an error will be reported in Java lang.ClassNotFoundException: org. apache. log4j. Priority #Then import log4j dependency. Maven address: https://mvnrepository.com/artifact/log4j/log4j filters: stat,wall,log4j maxPoolPreparedStatementPerConnectionSize: 20 useGlobalDataSourceStat: true connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500 mybatis: type-aliases-package: com.zmt.pojo mapper-locations: classpath:mapper/*.xml
- Preparation of User class
public class User { private int id; private String name; private String pwd; }
- Written by UserMapper interface
@Repository @Mapper public interface UserMapper { public User queryUserByName(String name); }
- UserMapper.xml Mapping
<?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"> <!--namespace=Bind a corresponding Dao/Mapper Interface--> <mapper namespace="com.zmt.mapper.UserMapper"> <select id="queryUserByName" parameterType="String" resultType="User"> select * from mybatis.user where name = #{name}; </select> </mapper>
- Userserve interface implementation
public interface UserService { public User queryUserByName(String name); }
- UserServiceImpl business logic
@Service public class UserServiceImpl implements UserService{ @Autowired UserMapper userMapper; @Override public User queryUserByName(String name) { return userMapper.queryUserByName(name); } }
- testing environment
@SpringBootTest class ShiroSpringbootApplicationTests { @Autowired UserService userService; @Test void contextLoads() { System.out.println(userService.queryUserByName("Li Si")); } }
- UserRealm connects to the real database
//Custom realm public class UserRealm extends AuthorizingRealm { @Autowired UserService userService; //to grant authorization @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { System.out.println("Authorization executed"); return null; } //authentication @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { System.out.println("Certification performed"); //Judge user name and password //Token is the user data token encapsulated in MyController UsernamePasswordToken userToken = (UsernamePasswordToken) token; //Connect to real database User user = userService.queryUserByName(userToken.getUsername()); if(user==null){ //UnknownAccountException return null; } //Password authentication Shiro does it. You don't have to write it yourself //The first parameter: obtain the authentication of the current user; The second parameter: the password to be passed; The third parameter: authentication name return new SimpleAuthenticationInfo("",user.getPwd(),""); } }
24.5 Shiro enables user authorization
- Add authorization statements in ShiroConfig. Note that they should be written before login interception
@Bean public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("getDefaultWebSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager){ ... //to grant authorization filterMap.put("/user/add","perms[user:add]"); //Add shiro's built-in filter to intercept login filterMap.put("/user/*","authc"); ... }
- Add unauthorized page in MyController
@RequestMapping("/noauth") @ResponseBody public String unauthorized() { return "This page cannot be accessed without authorization"; }
- Add a jump to the unauthorized page in the getShiroFilterFactoryBean() method in ShiroConfig
//Step 3: ShiroFilterFactoryBean @Bean public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("getDefaultWebSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager){ ... //If you do not have permission, jump to the login page bean.setLoginUrl("/toLogin"); //Set unauthorized page bean.setUnauthorizedUrl("/noauth"); return bean; }
-
Test, jump successful
-
To truly authorize users in UserRealm, you need to add the permission field perms in the user table and modify pojo
public class User { private int id; private String name; private String pwd; private String perms; }
- Modify the UserRealm class. The first parameter to be returned in the authentication method is user, return new SimpleAuthenticationInfo(user,user.getPwd(), "");
//Custom realm public class UserRealm extends AuthorizingRealm { @Autowired UserService userService; //to grant authorization @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { System.out.println("Authorization executed"); SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); //Get the currently logged in object Subject subject = SecurityUtils.getSubject(); //Get user object User currentUser = (User)subject.getPrincipal(); //Set current permissions info.addStringPermission(currentUser.getPerms()); return info; } //authentication @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { ... //Password authentication Shiro do, do not write their own //The first parameter: obtain the authentication of the current user; The second parameter: the password to be passed; The third parameter: authentication name return new SimpleAuthenticationInfo(user,user.getPwd(),""); } }
24.6 Shiro integrates Thymeleaf
- Modify ShiroConfig
//Integrate ShiroDialect: used to integrate Shiro and Thymeleaf @Bean public ShiroDialect getShiroDialect(){ return new ShiroDialect(); }
- pom.xml
<!--shiro-thymeleaf integration--> <dependency> <groupId>com.github.theborakompanioni</groupId> <artifactId>thymeleaf-extras-shiro</artifactId> <version>2.0.0</version> </dependency>
- Modify home page code
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.pollox.at/thymeleaf/shiro"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>home page</h1> <p> <a th:href="@{/toLogin}">Sign in</a> </p> <p th:text="${msg}"></p> <hr> <div shiro:hasPermission="user:add"> <a th:href="@{/user/add}">add</a> </div> <div shiro:hasPermission="user:update"> <a th:href="@{/user/update}">update</a> </div> </body> </html>
- The test is successful, and the add or update page will not be displayed within the time limit
24.7 complete code
- ShiroConfig
@Configuration public class ShiroConfig { //ShiroFilterFactoryBean,DefaultWebSecurityManager,realm //Step 1: create a realm object @Bean public UserRealm userRealm(){ return new UserRealm(); } //Step 2: DefaultWebSecurityManager @Bean public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){ DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); //Associate UserRealm securityManager.setRealm(userRealm); return securityManager; } //Step 3: ShiroFilterFactoryBean @Bean public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("getDefaultWebSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager){ ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean(); //Set up security manager bean.setSecurityManager(defaultWebSecurityManager); //Add shiro's built-in filter Map<String,String> filterMap = new LinkedHashMap<>(); //to grant authorization filterMap.put("/user/add","perms[user:add]"); filterMap.put("/user/update","perms[user:update]"); //Add shiro's built-in filter to intercept login filterMap.put("/user/*","authc"); // filterMap.put("/user/add","anon"); // filterMap.put("/user/update","authc"); bean.setFilterChainDefinitionMap(filterMap); //If you do not have permission, jump to the login page bean.setLoginUrl("/toLogin"); //Set unauthorized page bean.setUnauthorizedUrl("/noauth"); return bean; } //Integrate ShiroDialect: used to integrate Shiro and Thymeleaf @Bean public ShiroDialect getShiroDialect(){ return new ShiroDialect(); } }
- UserRealm
//Custom realm public class UserRealm extends AuthorizingRealm { @Autowired UserService userService; //to grant authorization @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { System.out.println("Authorization executed"); SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); //Get the currently logged in object Subject subject = SecurityUtils.getSubject(); //Get user object User currentUser = (User)subject.getPrincipal(); //Set current permissions info.addStringPermission(currentUser.getPerms()); return info; } //authentication @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { System.out.println("Certification performed"); //Judge user name and password //Token is the user data token encapsulated in MyController UsernamePasswordToken userToken = (UsernamePasswordToken) token; //Connect to real database User user = userService.queryUserByName(userToken.getUsername()); if(user==null){ //UnknownAccountException return null; } //Password authentication Shiro do, do not write their own //The first parameter: obtain the authentication of the current user; The second parameter: the password to be passed; The third parameter: authentication name return new SimpleAuthenticationInfo(user,user.getPwd(),""); } }
- MyController
@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"; } @RequestMapping("/login") public String login(String username, String password, Model model) { //Get a user Subject subject = SecurityUtils.getSubject(); // Encapsulate user login data UsernamePasswordToken token = new UsernamePasswordToken(username, password); try { //Execute the login method. If there is no exception, it indicates that the login is successful subject.login(token); 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"; } } @RequestMapping("/noauth") @ResponseBody public String unauthorized() { return "This page cannot be accessed without authorization"; } }
- pom.xml
<dependencies> <!--mysql--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <!--druid--> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.2.1</version> </dependency> <!--log4j--> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> <!--introduce mybatis,This is MyBatis Officially provided adaptations spring Boot Yes, not spring Boot own--> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.3</version> </dependency> <!--thymeleaf--> <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> <!--shiro integration spring My bag--> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.6.0</version> </dependency> <!--shiro-thymeleaf integration--> <dependency> <groupId>com.github.theborakompanioni</groupId> <artifactId>thymeleaf-extras-shiro</artifactId> <version>2.0.0</version> </dependency> </dependencies>