前言
- Apache Shiro 是一個(gè)強(qiáng)大易用的 Java 安全框架,提供了認(rèn)證、授權(quán)耐量、加密和會(huì)話管理等功能,對(duì)于任何一個(gè)應(yīng)用程序滤港,Shiro 都可以提供全面的安全管理服務(wù)廊蜒。并且相對(duì)于其他安全框架趴拧,Shiro 要簡(jiǎn)單的多。
- 三個(gè)核心組件:Subject山叮,SecurityManager 和 Realms.
Subject:即“當(dāng)前操作用戶”著榴。但是,在Shiro中屁倔,Subject這一概念并不僅僅指人脑又,也可以是第三方進(jìn)程、后臺(tái)帳戶(Daemon Account)或其他類似事物锐借。它僅僅意味著“當(dāng)前跟軟件交互的東西”问麸。
Subject代表了當(dāng)前用戶的安全操作,SecurityManager則管理所有用戶的安全操作钞翔。
SecurityManager:它是Shiro框架的核心严卖,典型的Facade模式,Shiro通過SecurityManager來管理內(nèi)部組件實(shí)例嗅战,并通過它來提供安全管理的各種服務(wù)妄田。
Realm: Realm充當(dāng)了Shiro與應(yīng)用安全數(shù)據(jù)間的“橋梁”或者“連接器”。也就是說驮捍,當(dāng)對(duì)用戶執(zhí)行認(rèn)證(登錄)和授權(quán)(訪問控制)驗(yàn)證時(shí)疟呐,Shiro會(huì)從應(yīng)用配置的Realm中查找用戶及其權(quán)限信息。Shiro可參考跟我學(xué)Shiro
一东且、pom.xml文件
這里添加了web启具、jpa、mysql珊泳、shiro和thymeleaf依賴鲁冯。jpa可參考SpringBoot 整合JPA,thymeleaf可參考SpringBoot 整合Thymeleaf色查。
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<nekohtml.version>1.9.22</nekohtml.version>
<shiro.version>1.4.0</shiro.version>
</properties>
<dependencies>
<!--web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--jpa-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!--shiro-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>${shiro.version}</version>
</dependency>
<!--thymeleaf-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!--HTML解析器和標(biāo)記平衡器薯演。-->
<dependency>
<groupId>net.sourceforge.nekohtml</groupId>
<artifactId>nekohtml</artifactId>
<version>${nekohtml.version}</version>
</dependency>
<!-- 調(diào)試熱啟動(dòng) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
二、配置文件
采用jpa作為數(shù)據(jù)庫持久層框架秧了,建表的任務(wù)交給框架自動(dòng)完成跨扮。
spring:
#數(shù)據(jù)庫配置
datasource:
url: jdbc:mysql://localhost:3306/test?serverTimezone=GMT
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
#jpa配置
jpa:
hibernate:
ddl-auto: update
show-sql: true
thymeleaf:
# 是否開啟模板緩存,默認(rèn)true
# 建議在開發(fā)時(shí)關(guān)閉緩存,不然沒法看到實(shí)時(shí)頁面
cache: false
##去除thymeleaf的html嚴(yán)格校驗(yàn)
mode: LEGACYHTML5
# 模板編碼
encoding: utf-8
#端口號(hào)
server:
port: 8080
三验毡、實(shí)體類
這里主鍵采用uuid方式衡创,ManyToMany做對(duì)應(yīng)關(guān)系。
- SysUser.java: 用戶實(shí)體類
@Entity
@Table(name = "sys_user")
public class SysUser implements Serializable {
@Id
@GeneratedValue(generator = "system-uuid")
@GenericGenerator(name = "system-uuid", strategy = "uuid")
private String id;
@Column(unique = true)
private String userName; //帳號(hào)
private String name; //名稱(昵稱或者真實(shí)姓名晶通,不同系統(tǒng)不同定義)
private String passWord; //密碼;
private String salt; //加密密碼的鹽
private byte state; //用戶狀態(tài),0:創(chuàng)建未認(rèn)證(比如沒有激活璃氢,沒有輸入驗(yàn)證碼等等)--等待驗(yàn)證的用戶 , 1:正常狀態(tài),2:用戶被鎖定.
//多對(duì)多關(guān)系
//急加載,加載一個(gè)實(shí)體時(shí)狮辽,定義急加載的屬性會(huì)立即從數(shù)據(jù)庫中加載
//FetchType.LAZY:懶加載一也,加載一個(gè)實(shí)體時(shí)巢寡,定義懶加載的屬性不會(huì)馬上從數(shù)據(jù)庫中加載
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(name = "SysUserRole", joinColumns = {@JoinColumn(name = "userId")}, inverseJoinColumns = {@JoinColumn(name = "roleId")})
private List<SysRole> roles; // 一個(gè)用戶具有多個(gè)角色
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassWord() {
return passWord;
}
public void setPassWord(String passWord) {
this.passWord = passWord;
}
public String getSalt() {
return salt;
}
public void setSalt(String salt) {
this.salt = salt;
}
public byte getState() {
return state;
}
public void setState(byte state) {
this.state = state;
}
public List<SysRole> getRoles() {
return roles;
}
public void setRoles(List<SysRole> roles) {
this.roles = roles;
}
//重新對(duì)鹽重新進(jìn)行了定義,用戶名+salt塘秦,這樣就更加不容易被破解
public String getCredentialsSalt() {
return this.userName + this.salt;
}
}
- SysRole.java:角色實(shí)體類
@Entity
@Table(name = "sys_role")
public class SysRole implements Serializable {
@Id
@GeneratedValue(generator = "system-uuid")
@GenericGenerator(name = "system-uuid", strategy = "uuid")
private String id; // 編號(hào)
private String role; // 角色標(biāo)識(shí)程序中判斷使用,如"admin",這個(gè)是唯一的:
private String description; // 角色描述,UI界面顯示使用
private Boolean available = Boolean.FALSE; // 是否可用,如果不可用將不會(huì)添加給用戶
//角色 -- 權(quán)限關(guān)系:多對(duì)多關(guān)系;
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(name = "SysRolePermission", joinColumns = {@JoinColumn(name = "roleId")}, inverseJoinColumns = {@JoinColumn(name = "permissionId")})
private List<SysPermission> permissions;
// 用戶 - 角色關(guān)系定義;
@ManyToMany
@JoinTable(name = "SysUserRole", joinColumns = {@JoinColumn(name = "roleId")}, inverseJoinColumns = {@JoinColumn(name = "userId")})
private List<SysUser> users;// 一個(gè)角色對(duì)應(yīng)多個(gè)用戶
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Boolean getAvailable() {
return available;
}
public void setAvailable(Boolean available) {
this.available = available;
}
public List<SysPermission> getPermissions() {
return permissions;
}
public void setPermissions(List<SysPermission> permissions) {
this.permissions = permissions;
}
public List<SysUser> getUsers() {
return users;
}
public void setUsers(List<SysUser> users) {
this.users = users;
}
}
- SysPermission:權(quán)限實(shí)體類
@Entity
@Table(name = "sys_permission")
public class SysPermission implements Serializable {
@Id
@GeneratedValue(generator = "system-uuid")
@GenericGenerator(name = "system-uuid", strategy = "uuid")
private String id; //主鍵.
private String name; //名稱.
@Column(columnDefinition = "enum('menu','button')")
private String resourceType; //資源類型讼渊,[menu|button]
private String url; //資源路徑.
private String permission; //權(quán)限字符串,menu例子:role:*,button例子:role:create,role:update,role:delete,role:view
private Long parentId; //父編號(hào)
private String parentIds; //父編號(hào)列表
private Boolean available = Boolean.FALSE;
@ManyToMany
@JoinTable(name = "SysRolePermission", joinColumns = {@JoinColumn(name = "permissionId")}, inverseJoinColumns = {@JoinColumn(name = "roleId")})
private List<SysRole> roles;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getResourceType() {
return resourceType;
}
public void setResourceType(String resourceType) {
this.resourceType = resourceType;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getPermission() {
return permission;
}
public void setPermission(String permission) {
this.permission = permission;
}
public Long getParentId() {
return parentId;
}
public void setParentId(Long parentId) {
this.parentId = parentId;
}
public String getParentIds() {
return parentIds;
}
public void setParentIds(String parentIds) {
this.parentIds = parentIds;
}
public Boolean getAvailable() {
return available;
}
public void setAvailable(Boolean available) {
this.available = available;
}
public List<SysRole> getRoles() {
return roles;
}
public void setRoles(List<SysRole> roles) {
this.roles = roles;
}
}
四尊剔、UserRepository類
創(chuàng)建一個(gè)UserRepository用于查詢用戶信息:
public interface UserRepository extends CrudRepository<SysUser, String> {
//通過username查找用戶信息;
SysUser findByUserName(String username);
}
五爪幻、插入數(shù)據(jù)
根據(jù)以上的代碼會(huì)自動(dòng)生成 sys_user(用戶信息表)、sys_role(角色表)须误、sys_permission(權(quán)限表)挨稿、sys_user_role(用戶角色表)、sys_role_permission(角色權(quán)限表)這五張表京痢,為了方便測(cè)試我們給這五張表插入一些初始化數(shù)據(jù):
INSERT INTO `sys_user` (`id`,`user_name`,`name`,`pass_word`,`salt`,`state`) VALUES ('1', 'admin', '管理員', 'd3c59d25033dbf980d29554025c23a75', '8d78869f470951332959580424d4bf4f', 0);
INSERT INTO `sys_permission` (`id`,`available`,`name`,`parent_id`,`parent_ids`,`permission`,`resource_type`,`url`) VALUES (1,0,'用戶管理',0,'0/','userInfo:view','menu','userInfo/userList');
INSERT INTO `sys_permission` (`id`,`available`,`name`,`parent_id`,`parent_ids`,`permission`,`resource_type`,`url`) VALUES (2,0,'用戶添加',1,'0/1','userInfo:add','button','userInfo/userAdd');
INSERT INTO `sys_permission` (`id`,`available`,`name`,`parent_id`,`parent_ids`,`permission`,`resource_type`,`url`) VALUES (3,0,'用戶刪除',1,'0/1','userInfo:del','button','userInfo/userDel');
INSERT INTO `sys_role` (`id`,`available`,`description`,`role`) VALUES (1,0,'管理員','admin');
INSERT INTO `sys_role` (`id`,`available`,`description`,`role`) VALUES (2,0,'VIP會(huì)員','vip');
INSERT INTO `sys_role` (`id`,`available`,`description`,`role`) VALUES (3,1,'test','test');
INSERT INTO `sys_role_permission` VALUES ('1', '1');
INSERT INTO `sys_role_permission` (`permission_id`,`role_id`) VALUES (1,1);
INSERT INTO `sys_role_permission` (`permission_id`,`role_id`) VALUES (2,1);
INSERT INTO `sys_role_permission` (`permission_id`,`role_id`) VALUES (3,2);
INSERT INTO `sys_user_role` (`role_id`,`user_id`) VALUES (1,1);
六奶甘、Shiro 配置
- ShiroConfig
首先要配置的是 ShiroConfig 類,Apache Shiro 核心通過 Filter 來實(shí)現(xiàn)臭家,就好像 SpringMvc 通過 DispachServlet 來主控制一樣。Shiro 通過一系列filter來控制訪問權(quán)限钉赁,并在它的內(nèi)部為我們預(yù)先定義了多個(gè)過濾器,我們可以直接通過字符串配置這些過濾器你踩。
Filter Chain 定義說明:
1、一個(gè)URL可以配置多個(gè) Filter带膜,使用逗號(hào)分隔
2、當(dāng)設(shè)置多個(gè)過濾器時(shí)鸳谜,全部驗(yàn)證通過膝藕,才視為通過
3、部分過濾器可指定參數(shù)咐扭,如 perms束莫,roles
常用的過濾器如下:
authc:需要認(rèn)證才能進(jìn)行訪問
roles:有指定角色的用戶可訪問,通過[ ]指定具體角色草描,這里的角色名稱與數(shù)據(jù)庫中配置一致
perms:有指定權(quán)限的用戶可訪問,通過[ ]指定具體權(quán)限策严,這里的權(quán)限名稱與數(shù)據(jù)庫中配置一致
anon:所有用戶可訪問穗慕,通常作為指定頁面的靜態(tài)資源時(shí)使用
user:配置記住我或認(rèn)證通過可以訪問
@Configuration
public class ShiroConfig {
@Bean
public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
System.out.println("ShiroConfiguration.shirFilter()");
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
//shiro攔截器
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
// 配置不被攔截的資源及鏈接 順序判斷
filterChainDefinitionMap.put("/static/**", "anon");
filterChainDefinitionMap.put("/favicon.ico", "anon");
//配置退出 過濾器,其中的具體的退出代碼Shiro已經(jīng)替我們實(shí)現(xiàn)了
filterChainDefinitionMap.put("/logout", "logout");
//<!-- 過濾鏈定義,從上向下順序執(zhí)行妻导,一般將/**放在最為下邊 -->: 這是一個(gè)坑呢逛绵,一不小心代碼就不好使了;
//<!-- authc:所有url都必須認(rèn)證通過才可以訪問; anon:所有url都都可以匿名訪問-->
filterChainDefinitionMap.put("/**", "authc");
// 如果不設(shè)置默認(rèn)會(huì)自動(dòng)尋找Web工程根目錄下的"/login.jsp"頁面
shiroFilterFactoryBean.setLoginUrl("/login");
// 登錄成功后要跳轉(zhuǎn)的鏈接
shiroFilterFactoryBean.setSuccessUrl("/index");
//未授權(quán)界面;
shiroFilterFactoryBean.setUnauthorizedUrl("/403");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
/**
* 憑證匹配器
* (由于我們的密碼校驗(yàn)交給Shiro的SimpleAuthenticationInfo進(jìn)行處理了
* )
*
* @return
*/
@Bean
public HashedCredentialsMatcher hashedCredentialsMatcher() {
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
hashedCredentialsMatcher.setHashAlgorithmName("md5");//散列算法:這里使用MD5算法;
hashedCredentialsMatcher.setHashIterations(2);//散列的次數(shù)怀各,比如散列兩次,相當(dāng)于 md5(md5(""));
return hashedCredentialsMatcher;
}
//自定義身份認(rèn)證Realm(包含用戶名密碼校驗(yàn)术浪,權(quán)限校驗(yàn)等)
@Bean
public MyShiroRealm myShiroRealm() {
MyShiroRealm myShiroRealm = new MyShiroRealm();
myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
return myShiroRealm;
}
@Bean
public SecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(myShiroRealm());
return securityManager;
}
//開啟shiro aop注解支持瓢对,不開啟的話權(quán)限驗(yàn)證就會(huì)失效
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
//配置異常處理,不配置的話沒有權(quán)限后臺(tái)報(bào)錯(cuò)胰苏,前臺(tái)不會(huì)跳轉(zhuǎn)到403頁面
@Bean(name = "simpleMappingExceptionResolver")
public SimpleMappingExceptionResolver
createSimpleMappingExceptionResolver() {
SimpleMappingExceptionResolver simpleMappingExceptionResolver = new SimpleMappingExceptionResolver();
Properties mappings = new Properties();
mappings.setProperty("DatabaseException", "databaseError");//數(shù)據(jù)庫異常處理
mappings.setProperty("UnauthorizedException", "403");
simpleMappingExceptionResolver.setExceptionMappings(mappings); // None by default
simpleMappingExceptionResolver.setDefaultErrorView("error"); // No default
simpleMappingExceptionResolver.setExceptionAttribute("ex"); // Default is "exception"
return simpleMappingExceptionResolver;
}
}
- 自定義Realm 類
1硕蛹、自定義一個(gè) Realm 類,繼承AuthorizingRealm 抽象類硕并,重載 doGetAuthenticationInfo()法焰,重寫獲取用戶信息的方法。
2倔毙、Shiro 的權(quán)限授權(quán)是通過繼承AuthorizingRealm抽象類埃仪,重載doGetAuthorizationInfo();當(dāng)訪問到頁面的時(shí)候陕赃,鏈接配置了相應(yīng)的權(quán)限或者 Shiro 標(biāo)簽才會(huì)執(zhí)行此方法否則不會(huì)執(zhí)行卵蛉,所以如果只是簡(jiǎn)單的身份認(rèn)證沒有權(quán)限的控制的話,那么這個(gè)方法可以不進(jìn)行實(shí)現(xiàn)么库,直接返回 null 即可
public class MyShiroRealm extends AuthorizingRealm {
@Resource
private UserRepository userRepository;
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
System.out.println("權(quán)限配置-->MyShiroRealm.doGetAuthorizationInfo()");
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
SysUser user = (SysUser) principals.getPrimaryPrincipal();
for (SysRole role : user.getRoles()) {
authorizationInfo.addRole(role.getRole());
for (SysPermission permission : role.getPermissions()) {
authorizationInfo.addStringPermission(permission.getPermission());
}
}
return authorizationInfo;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token)
throws AuthenticationException {
System.out.println("MyShiroRealm.doGetAuthenticationInfo()");
//獲得當(dāng)前用戶的用戶名
String username = (String) token.getPrincipal();
System.out.println(token.getCredentials());
//根據(jù)用戶名找到對(duì)象
//實(shí)際項(xiàng)目中傻丝,這里可以根據(jù)實(shí)際情況做緩存桑滩,如果不做允睹,Shiro自己也是有時(shí)間間隔機(jī)制缭受,2分鐘內(nèi)不會(huì)重復(fù)執(zhí)行該方法
SysUser user = userRepository.findByUserName(username);
// System.out.println("----->>userInfo=" + user);
if (user == null) {
return null;
}
//這里會(huì)去校驗(yàn)密碼是否正確
SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
user, //用戶名
user.getPassWord(), //密碼
ByteSource.Util.bytes(user.getCredentialsSalt()),//salt=username+salt
getName() //realm name
);
return authenticationInfo;
}
}
七米者、Controller類
- 登錄實(shí)現(xiàn)
@Controller
public class LoginController {
@GetMapping({"/", "/index"})
public String index() {
return "index";
}
@RequestMapping("/login")
public String login(HttpServletRequest request, Map<String, Object> map) throws Exception {
System.out.println("HomeController.login()");
// 登錄失敗從request中獲取shiro處理的異常信息蔓搞。
// shiroLoginFailure:就是shiro異常類的全類名.
String exception = (String) request.getAttribute("shiroLoginFailure");
String msg = "";
//根據(jù)異常判斷錯(cuò)誤類型
if (exception != null) {
if (UnknownAccountException.class.getName().equals(exception)) {
System.out.println("UnknownAccountException -- > 賬號(hào)不存在:");
msg = "UnknownAccountException -- > 賬號(hào)不存在:";
} else if (IncorrectCredentialsException.class.getName().equals(exception)) {
System.out.println("IncorrectCredentialsException -- > 密碼不正確:");
msg = "IncorrectCredentialsException -- > 密碼不正確:";
} else if ("kaptchaValidateFailed".equals(exception)) {
System.out.println("kaptchaValidateFailed -- > 驗(yàn)證碼錯(cuò)誤");
msg = "kaptchaValidateFailed -- > 驗(yàn)證碼錯(cuò)誤";
} else {
msg = "else >> " + exception;
System.out.println("else -- >" + exception);
}
}
map.put("msg", msg);
// 此方法不處理登錄成功,由shiro進(jìn)行處理
return "/login";
}
@RequestMapping("/403")
public String unauthorizedRole() {
System.out.println("------沒有權(quán)限-------");
return "/sys/403";
}
}
- 權(quán)限測(cè)試類
@Controller
@RequestMapping("/user")
public class UserController {
/**
* 用戶查詢.
* @return
*/
@RequestMapping("/userList")
@RequiresPermissions("user:view")//權(quán)限管理;
public String user(){
return "user";
}
/**
* 用戶添加;
* @return
*/
@RequestMapping("/userAdd")
@RequiresPermissions("user:add")//權(quán)限管理;
public String userAdd(){
return "userAdd";
}
/**
* 用戶刪除;
* @return
*/
@RequestMapping("/userDel")
@RequiresPermissions("user:del")//權(quán)限管理;
public String userDel(){
return "userDel";
}
}
八锦庸、html頁面
- login.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>Login</title>
</head>
<body>
錯(cuò)誤信息:<h4 th:text="${msg}"></h4>
<form action="" method="post">
<p>賬號(hào):<input type="text" name="username" value="admin"/></p>
<p>密碼:<input type="text" name="password" value="123456"/></p>
<p><input type="submit" value="登錄"/></p>
</form>
</body>
</html>
- index.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>index</title>
</head>
<body>
<h1>index</h1>
<a href="/user/userList">用戶列表</a>
<a href="/user/userAdd">用戶添加</a>
<a href="/user/userDel">用戶刪除</a>
<form th:action="@{/logout}" method="post">
<p><input type="submit" value="注銷"/></p>
</form>
</body>
</html>
- user.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>UserInfo</title>
</head>
<body>
<h3>用戶查詢界面</h3>
</body>
</html>
- userAdd.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Add</title>
</head>
<body>
<h3>用戶添加界面</h3>
</body>
</html>
- userDel.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Del</title>
</head>
<body>
<h3>用戶刪除界面</h3>
</body>
</html>
- 403.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>403</title>
</head>
<body>
<h3>403沒有權(quán)限</h3>
</body>
</html>