2024.5.28 Tuesday
Continuation from previous 【WEEK14】 【DAY1】Shiro Part 6【English Version】
Contents
- 15.8. Integrate Shiro with Thymeleaf
15.8. Integrate Shiro with Thymeleaf
15.8.1. Modify pom.xml to Add Dependencies
15.8.1.1. Importing the shiro-thymeleaf Integration Package
Official Website: Maven Repository: com.github.theborakompanioni » thymeleaf-extras-shiro (mvnrepository.com)
<!-- 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>
15.8.1.2. Current Complete pom.xml File
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.13</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>shiro-springboot</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>shiro-springboot</name>
<description>shiro-springboot</description>
<properties>
<java.version>8</java.version>
</properties>
<dependencies>
<!--
Subject 用户
SecurityManager 管理所有用户
Realm 连接数据
-->
<!--shiro_thymeleaf Integration-->
<!-- 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>
<!--mysql-->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
</dependency>
<!--log4j-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!--druid-->
<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.21</version>
</dependency>
<!--mybatis-->
<!-- 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.2.2</version>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.20</version>
</dependency>
<!--shiro Integration with Spring packages-->
<!-- 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>
<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>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
15.8.2. Modify ShiroConfig.java
package com.P40.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;
@Configuration
public class ShiroConfig {
//ShiroFilterFactoryBean
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager){
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
//What to configure: click to see the source code of ShiroFilterFactoryBean
//Set security manager
bean.setSecurityManager(defaultWebSecurityManager);
//Add Shiro's built-in filters
/*
anon: Accessible without authentication
authc: Requires authentication to access
user: Must have 'remember me' feature enabled
perms: Must have permission for a specific resource to access
role: Must have permission for a specific role
*/
//Login interception
Map<String,String> filterMap = new LinkedHashMap<>();
//Authorization. Normally, it should redirect to an unauthorized page, but at this point, due to only adding the following verifications, it directly jumps to the 401 page.
filterMap.put("/user/add","perms[user:add]");
filterMap.put("/user/update","perms[user:update]");
// filterMap.put("/user/add","authc");
// filterMap.put("/user/update","authc");
//After modifying the access permissions of the add and update pages only here, restart the project, and clicking add or update will be intercepted, displaying a 404 error, hoping to redirect to the login page
filterMap.put("/user/*","authc"); //Wildcards can also be used here (instead of /user/add and /user/update from the above two lines)
bean.setFilterChainDefinitionMap(filterMap);
//Redirect to login page if not authenticated
bean.setLoginUrl("/toLogin"); //Set the login request
//Unauthorized page
bean.setUnauthorizedUrl("/noauth");
return bean;
}
//DefaultWebSecurityManager
@Bean(name = "securityManager") //Alias this class to facilitate calls by ShiroFilterFactoryBean
public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){ //Get UserRealm, but it seems that annotations are not needed here, it can be called directly
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//The default class name of DefaultWebSecurityManager is defaultWebSecurityManager, just changed to securityManager here
//Associate UserRealm
securityManager.setRealm(userRealm);
return securityManager;
}
//Create realm object, need to customize the class
@Bean
public UserRealm userRealm(){
return new UserRealm();
}
//Creation order is reversed (from real->DefaultWebSecurityManager->ShiroFilterFactoryBean)
//Integrate ShiroDialect (dialect): Used to integrate Shiro and Thymeleaf
@Bean
public ShiroDialect getShiroDialect(){
return new ShiroDialect();
}
}
15.8.3. Modify index.html
<!DOCTYPE html>
<html lang="en" xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>Homepage</h1>
<!--Display "Login" entry only when not logged in-->
<div shiro:notAuthenticated>
<a th:href="@{/toLogin}">Login</a>
</div>
<p th:text="${msg}"></p>
<hr> <!--Create a horizontal line in the HTML page-->
<!--Display the functionality entry on the page only when the user has corresponding permissions in the database-->
<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>
<!--a tag defines a hyperlink, used to link from one page to another-->
<!--Display "Sign out" entry only when logged in-->
<hr>
<div shiro:Authenticated>
<a class="nav-link" th:href="@{/logout}" onclick="alert('Logged out')">Sign out</a><!--Make sure not to misspell href as herf-->
</div>
</body>
</html>
15.8.4. Open all permissions for the root user (just for experimentation)
15.8.5. Modify MyController.java
Only the logout method is modified.
//Logout
@RequestMapping("/logout")
public String logout(){
Subject currentUser = SecurityUtils.getSubject();
currentUser.logout();
return "/index"; //Modified to: Redirect to the homepage after logout
}
15.8.6. Restart
15.8.6.1. When no user is logged in
15.8.6.2. Click on Login
Only display the functionalities that the logged-in user has permission to access on the homepage
15.8.6.3. Click on Sign out
Changed to return to the main page (directly redirected to the login page when requesting authorization)