2024.5.27 Monday
Contents
15.7. Implementation of Shiro Request Authorization
Continuing from the previous section【WEEK13】 【DAY5】Shiro Part 5【English Version】
15.7.6. Modify Database Related Parameters to Use Real Database Data
15.7.6.1. Modify the Database
In reality, whether a user has access to a particular functionality (page) should be stored in the database. The program should then read it and determine whether the corresponding page is accessible to the specified user. The method of adding authorization using the addStringPermission
method in the UserRealm
class in the previous section is not reasonable. Also, because the current database lacks a column for user permissions, a new column needs to be added here.
Grant permissions to users respectively:
Lisi and Wangwu have no permissions, and the perms
column cannot be left empty directly. It needs to be filled with null
or user:null
. Otherwise, when attempting to access add
or update
, the error page that appears is still not the modified prompt statement but the default 401
page.
15.7.6.2. Modify User.java
Add one parameter
package com.P40.pojo;
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;
}
15.7.7. Modify UserRealm.java
package com.P40.config;
import com.P40.pojo.User;
import com.P40.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;
//UserRealm is a bean
//Customized UserRealm, it must inherit AuthorizingRealm method, and then implement methods (alt+insert)
public class UserRealm extends AuthorizingRealm {
@Autowired
UserService userService;
//Authorization
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("do doGetAuthorizationInfo Authorization");
//Authorization
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); //You can first write new SimpleAuthorizationInfo(); and then use Alt+Enter to quickly create the preceding framework
// info.addStringPermission("user:add"); //Give all logged-in users permission to access User:add, at this time, any logged-in user can access the add page
//Get the currently logged-in object
Subject subject = SecurityUtils.getSubject(); //Represents the currently logged-in user
User currentUser = (User) subject.getPrincipal(); //Take out the first parameter (user) from SimpleAuthenticationInfo(user,user.getPwd(),""); and get the user object
//Set permissions for the current user
info.addStringPermission(currentUser.getPerms());
//Cannot return null
return info;
}
//Authentication
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("do doGetAuthenticationInfo Authentication");
UsernamePasswordToken userToken = (UsernamePasswordToken) authenticationToken;
//Change to connect to the real database
User user = userService.queryUserByName(userToken.getUsername());
if(user == null){ //This user does not exist
return null; //UnknownAccountException
}
//Password authentication, shiro operation
return new SimpleAuthenticationInfo(user,user.getPwd(),"");
}
}
15.7.8. Modify ShiroConfig.java
Add authorization
package com.P40.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;
@Configuration
public class ShiroConfig {
//ShiroFilterFactoryBean
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager){
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
//What to configure: click to enter ShiroFilterFactoryBean source code for viewing
//Set security manager
bean.setSecurityManager(defaultWebSecurityManager);
//Add Shiro's built-in filters
/*
anon: Allows access without authentication
authc: Requires authentication to access
user: Requires the Remember Me function to be enabled
perms: Requires permission to access a specific resource
role: Requires permission for a specific role
*/
//Login interception
Map<String,String> filterMap = new LinkedHashMap<>();
//Authorization. Normally, it should redirect to an unauthorized page, but because only the following verifications are added, it leads to direct redirection 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 for the add and update pages only here, restart the project, and clicking add or update will be intercepted, showing a 404 error, hoping to redirect to the login page
filterMap.put("/user/*","authc"); //Wildcards can also be used to achieve (replacing the above two lines of /user/add and /user/update)
bean.setFilterChainDefinitionMap(filterMap);
//If no permission, it needs to be redirected to the login page
bean.setLoginUrl("/toLogin"); //Set the login request
//Unauthorized page
bean.setUnauthorizedUrl("/noauth");
return bean;
}
//DefaultWebSecurityManager
@Bean(name = "securityManager") //Alias this class for easy invocation by ShiroFilterFactoryBean
public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){ //Get UserRealm, but it seems that annotations are not needed here, and 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 class
@Bean
public UserRealm userRealm(){
return new UserRealm();
}
//Creation order is reversed (from real->DefaultWebSecurityManager->ShiroFilterFactoryBean)
}
15.7.9. Add Logout Functionality
15.7.9.1. Modify index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>Home</h1>
<p th:text="${msg}"></p>
<hr> <!--Create a horizontal line in the HTML page-->
<a th:href="@{/user/add}">add</a> | <a th:href="@{/user/update}">update</a>
<!--a tag defines a hyperlink for linking from one page to another-->
<hr>
<a class="nav-link" th:href="@{/logout}" onclick="alert('Signed out')">Sign out</a><!--Don't spell href as herf-->
</body>
</html>
15.7.9.2. Modify MyController.java
package com.P40.controller;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
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.ResponseBody;
import javax.servlet.http.HttpSession;
@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 the current user
Subject subject = SecurityUtils.getSubject();
//Encapsulate the user's login data
UsernamePasswordToken token = new UsernamePasswordToken(username,password);
//Execute the login method, if there is no exception, it is successful, select the code and press Ctrl+Alt+T to add try catch
try {
subject.login(token);
return "index";
} catch (UnknownAccountException e) { //Username does not exist
model.addAttribute("msg","Incorrect username");
return "login";
} catch (IncorrectCredentialsException e) { //Wrong password
model.addAttribute("msg","Incorrect password");
return "login";
}
}
@RequestMapping("/noauth")
@ResponseBody
public String unauthorized(){
return "Unauthorized, access to this page is forbidden";
}
//Logout
@RequestMapping("/logout")
public String logout(){
Subject currentUser = SecurityUtils.getSubject();
currentUser.logout();
return "/login";
}
}
15.7.10. Restart and Verify Permissions for Respective Users and Logout Functionality
15.7.10.1. zhangsan
Login:
Click on “add”:
Click on “update”:
Click on “Sign out”:
15.7.10.2. lisi
Login:
Click on “add”:
Click on “update”:
Click on “Sign out”:
15.7.10.3. wangwu
Login:
Click on “add”:
Click on “update”:
Click on “Sign out”:
15.7.10.4. root
Login:
Click on “add”:
Click on “update”:
Click on “Sign out”: