上篇:springBoot使用springSecurity入門詳細配置,自定義登錄,RBAC權限模型,數據庫(mybatis+mysql)驗證登錄,驗證碼,json格式交互等(上篇)
寫了:
- springBoot項目使用security
- 配置介紹
- 自定義登錄頁面
- 自定義登陸成功失敗處理器
- 添加驗證碼驗證過濾
下篇開始
1.自定義用戶認證的數據來源,內存以及數據庫
前面我們一直使用security默認提供的user賬號以及啓動生產來登錄,現在我們來配置自己用戶信息;
security用戶數據的配置在前面也已經介紹了,即配置類重寫configure(AuthenticationManagerBuilder auth)方法
下面將介紹兩種數據來源
- 直接配置在內存中
這種比較簡單,直接在配置文件yml配置即可spring: security: user: name: admin password: admin
成功!
- 配置數據庫來源
更多的時候我們的用戶數據是放在數據庫中,我們就需要配置數據庫來源了,配置類重寫configure(AuthenticationManagerBuilder auth)指定我們自定義數據來源;
數據庫:我使用的是mysql
持久層框架:使用springJpa,之前一直使用的是mybatis,最近實習發現公司使用JPA比較多,所以這次順便學學使用JPA
1.使用Jpa,並使用其自動創建表功能,準備好數據庫數據
添加依賴<!--druid數據庫連接池--> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.10</version> </dependency> <!--mysql--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.35</version> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency>
配置Jpa以及數據庫連接池spring: datasource: type: com.alibaba.druid.pool.DruidDataSource driver: com.mysql.jdbc.Driver druid: username: password: url: jdbc:mysql://localhost:3306/security?useUnicode=true&characterEncoding=utf-8&useSSL=false initial-size: 1 max-active: 5 min-idle: 1 jpa: show-sql: true #顯示sql語句 hibernate: ddl-auto: update #自動建表 open-in-view: true properties: hibernate: enable_lazy_load_no_trans: true #將Jpa的session生命週期擴大
@Data @Entity @Table(name = "user", uniqueConstraints = {@UniqueConstraint(columnNames = {"username"}), @UniqueConstraint(columnNames = {"email"})}) public class User implements UserDetails { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer u_id; @Column(nullable = false) @Length(min = 3, max = 20) private String username; @Column(nullable = false,length = 100) @JsonIgnore private String password; @Column(nullable = false) @Length(min = 3, max = 20) private String realName; @Column(nullable = false, length = 50) private String email; @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") private Date registerDate; @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") private Date lastLogin; private boolean isEnabled=true; private boolean isAccountNonExpired=true; private boolean isAccountNonLocked=true; private boolean isCredentialsNonExpired=true; @ManyToMany @JoinTable(name = "user_role", joinColumns = @JoinColumn(name = "u_id"), inverseJoinColumns = @JoinColumn(name = "r_id")) private List<Role> roleList; // 存儲用戶的權限 @Transient private List<GrantedAuthority> authorities; @Override public boolean equals(Object obj) { if (this == obj) return true; if (!(obj instanceof User)) return false; User user = (User) obj; boolean boo = this.username.equals(user.getUsername()); return boo; } @Override public int hashCode() { return username != null ? username.hashCode() : 0; } @Override public String toString() { return "User{" + "u_id=" + u_id + ", username='" + username + '\'' + ", password='" + password + '\'' + ", realName='" + realName + '\'' + ", email='" + email + '\'' + ", registerDate=" + registerDate + ", lastLogin=" + lastLogin + ", isEnabled=" + isEnabled + ", isAccountNonExpired=" + isAccountNonExpired + ", isAccountNonLocked=" + isAccountNonLocked + ", isCredentialsNonExpired=" + isCredentialsNonExpired + ", roleList=" + roleList + ", authorities=" + authorities + '}'; } }
SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS = 0; -- ---------------------------- -- Table structure for user -- ---------------------------- DROP TABLE IF EXISTS `user`; CREATE TABLE `user` ( `u_id` int(11) NOT NULL AUTO_INCREMENT, `email` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, `is_account_non_expired` bit(1) NOT NULL, `is_account_non_locked` bit(1) NOT NULL, `is_credentials_non_expired` bit(1) NOT NULL, `is_enabled` bit(1) NOT NULL, `last_login` datetime NULL DEFAULT NULL, `password` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, `real_name` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, `register_date` datetime NULL DEFAULT NULL, `username` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, PRIMARY KEY (`u_id`) USING BTREE, UNIQUE INDEX `UKsb8bbouer5wak8vyiiy4pf2bx`(`username`) USING BTREE, UNIQUE INDEX `UKob8kqyqqgmefl0aco34akdtpe`(`email`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Compact; -- ---------------------------- -- Records of user -- ---------------------------- INSERT INTO `user` VALUES (1, '[email protected]', b'1', b'1', b'1', b'1', NULL, '$2a$10$RaDnhjsjVAoXE9DuOJxPV.BhJ0uX5oBtRJqvn6PSjQR7ASvZ53Z.a', 'admin', NULL, 'admin'); SET FOREIGN_KEY_CHECKS = 1;
Jpa在測試類添加一個用戶,mybatis使用上面添加一條用戶的跳過這個@SpringBootTest public class SecurityApplicationTests { @Autowired UserRepository userRepository; // @Autowired // RoleRepository roleRepository; // @Autowired // PermissionRepository permissionRepository; @Test void contextLoads() { BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder(); User user = new User(); user.setUsername("admin"); user.setPassword(bCryptPasswordEncoder.encode("admin")); user.setEmail("[email protected]"); user.setRealName("admin"); // Permission adminPermission = new Permission(); // adminPermission.setPermName("管理員用戶權限"); // adminPermission.setPermTag("ROLE_ADMIN"); // Permission commonPermission = new Permission(); // commonPermission.setPermName("普通用戶權限"); // commonPermission.setPermTag("ROLE_COMMON"); // permissionRepository.save(adminPermission); // permissionRepository.save(commonPermission); // // List<Permission> permissions = new ArrayList<>(); // // permissions.add(commonPermission); // Role commonRole= new Role(); // commonRole.setRoleName("普通用戶"); // commonRole.setRoleDesc("普通用戶角色"); // commonRole.setPermissionList(permissions); // roleRepository.save(commonRole); // // permissions.add(adminPermission); // Role adminRole= new Role(); // adminRole.setRoleName("管理員用戶"); // adminRole.setRoleDesc("管理員用戶角色"); // adminRole.setPermissionList(permissions); // roleRepository.save(adminRole); // // List<Role> roles = new ArrayList<>(); // // roles.add(adminRole); // user.setRoleList(roles); userRepository.save(user); System.out.println(user); System.out.println(userRepository.findAll()); } }
數據庫數據準備完畢
1.自定義數據來源,將數據庫的用戶提供給security,供其驗證登陸
jpa接口public interface UserRepository extends JpaRepository<User,Integer> { User findByUsername(String username); }
//security自定義驗證登陸用戶時的數據來源,從數據庫獲取 @Component public class MyUserDetailService implements UserDetailsService { @Autowired UserRepository userRepository; /** * 根據用戶名獲取用戶信息 * * @param s 用戶名 * @return User 返回一個用戶信息 * @throws UsernameNotFoundException */ @Override public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException { User user = userRepository.findByUsername(s); System.out.println(user); // if (user != null) { // List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>(); // for (Role role : user.getRoleList()) { // for (Permission p : role.getPermissionList()) { // GrantedAuthority authority = new SimpleGrantedAuthority(p.getPermTag()); // authorities.add(authority); // } // //將權限集合放進user // user.setAuthorities(authorities); // System.out.println("當前用戶是: " + user.getUsername()); // } // } return user; } }
將該實現類配置給security,在配置類重寫void configure(AuthenticationManagerBuilder auth)
@Autowired private MyUserDetailService myUserDetailService; @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Override public void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(myUserDetailService).passwordEncoder(passwordEncoder()); }
ok!啓動項目
成功!!
2.RBAC權限模型
可以看出前面所有用戶都是一樣的,這樣的功能有點單調,如果我們希望用戶是有所區別,就像windows用戶組一樣,我們希望不同的用戶擁有不同的權限,因爲我們網站不同模塊有些對所有人開放,有些只對管理員開放,有些只對vip用戶開放;這樣就需要RBAC權限模型了
關於概念理念可以看看:
https://www.cnblogs.com/aoxueshou/p/10115359.html
我們就開始我們代碼:三張基本表user、role、permission,以及闡述之間關係多對多的兩張中間表,E-R圖如下
1.JPA的Entity:
user:
//implements UserDetails
@Data
@Entity
@Table(name = "user",
uniqueConstraints = {@UniqueConstraint(columnNames = {"username"}), @UniqueConstraint(columnNames = {"email"})})
public class User implements UserDetails {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer u_id;
@Column(nullable = false)
@Length(min = 3, max = 20)
private String username;
@Column(nullable = false,length = 100)
@JsonIgnore
private String password;
@Column(nullable = false)
@Length(min = 3, max = 20)
private String realName;
@Column(nullable = false, length = 50)
private String email;
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date registerDate;
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date lastLogin;
private boolean isEnabled=true;
private boolean isAccountNonExpired=true;
private boolean isAccountNonLocked=true;
private boolean isCredentialsNonExpired=true;
@ManyToMany
@JoinTable(name="user_role",joinColumns = @JoinColumn(name="u_id"),
inverseJoinColumns = @JoinColumn(name="r_id"))
List<Role> roleList;
@Transient
private List<GrantedAuthority> authorities;
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (!(obj instanceof User)) return false;
User user = (User) obj;
boolean boo = this.username.equals(user.getUsername());
return boo;
}
@Override
public int hashCode() {
return username != null ? username.hashCode() : 0;
}
}
role:
@Data
@Entity
@Table(name = "role",uniqueConstraints = {@UniqueConstraint(columnNames = {"roleName"})})
public class Role {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer r_id;
@Column(nullable = false)
@Length(max = 20)
private String roleName;
@Column(nullable = false)
@Length(max = 20)
private String roleDesc;
@ManyToMany
@JoinTable(name="role_permission",joinColumns = @JoinColumn(name="r_id"),
inverseJoinColumns = @JoinColumn(name="p_id"))
private List<Permission> permissionList;
public Role() {
}
}
permission:
@Data
@Entity
@Table(name = "permission",uniqueConstraints = {@UniqueConstraint(columnNames = {"permName"}),@UniqueConstraint(columnNames = {"permTag"})})
public class Permission implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer p_id;
@Column(nullable = false)
@Length(max = 20)
private String permName;
@Column(nullable = false)
@Length(max = 20)
private String permTag;
public Permission(){
}
}
啓動項目,自動創建如ER圖裏面對應的表
2.將數據庫之前插入的用戶刪了,然後把測試類的方法註釋全部弄掉運行測試方法,插入一個RBAC模型下的一個用戶
啓動測試方法,插入一個admin用戶,兩個角色(管理員,普通用戶),兩個權限(管理員權限,普通權限),admin用戶擁有管理員角色,管理員角色擁有管理員權限以及普通權限;
mybatis就需要自己根據下面sql弄了:
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`u_id` int(11) NOT NULL AUTO_INCREMENT,
`email` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
`is_account_non_expired` bit(1) NOT NULL,
`is_account_non_locked` bit(1) NOT NULL,
`is_credentials_non_expired` bit(1) NOT NULL,
`is_enabled` bit(1) NOT NULL,
`last_login` datetime NULL DEFAULT NULL,
`password` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
`real_name` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
`register_date` datetime NULL DEFAULT NULL,
`username` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
PRIMARY KEY (`u_id`) USING BTREE,
UNIQUE INDEX `UKsb8bbouer5wak8vyiiy4pf2bx`(`username`) USING BTREE,
UNIQUE INDEX `UKob8kqyqqgmefl0aco34akdtpe`(`email`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Compact;
-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES (2, '[email protected]', b'1', b'1', b'1', b'1', NULL, '$2a$10$sm2TYwjob0JCIDrKUBpBvusSDeUbhjn/4dzjYpoKtyusBBlW1fnF2', 'admin', NULL, 'admin');
SET FOREIGN_KEY_CHECKS = 1;
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for role
-- ----------------------------
DROP TABLE IF EXISTS `role`;
CREATE TABLE `role` (
`r_id` int(11) NOT NULL AUTO_INCREMENT,
`role_desc` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
`role_name` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
PRIMARY KEY (`r_id`) USING BTREE,
UNIQUE INDEX `UKbgeqjb5opmijvwc14fbtaj4xx`(`role_name`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Compact;
-- ----------------------------
-- Records of role
-- ----------------------------
INSERT INTO `role` VALUES (1, '普通用戶角色', '普通用戶');
INSERT INTO `role` VALUES (2, '管理員用戶角色', '管理員用戶');
SET FOREIGN_KEY_CHECKS = 1;
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for user_role
-- ----------------------------
DROP TABLE IF EXISTS `user_role`;
CREATE TABLE `user_role` (
`u_id` int(11) NOT NULL,
`r_id` int(11) NOT NULL,
INDEX `FKto8gqveqi41eyylx7a2tqlfip`(`r_id`) USING BTREE,
INDEX `FKhqbsm81qe5n0g3phrjs0kucos`(`u_id`) USING BTREE,
CONSTRAINT `FKhqbsm81qe5n0g3phrjs0kucos` FOREIGN KEY (`u_id`) REFERENCES `user` (`u_id`) ON DELETE RESTRICT ON UPDATE RESTRICT,
CONSTRAINT `FKto8gqveqi41eyylx7a2tqlfip` FOREIGN KEY (`r_id`) REFERENCES `role` (`r_id`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Compact;
-- ----------------------------
-- Records of user_role
-- ----------------------------
INSERT INTO `user_role` VALUES (2, 2);
SET FOREIGN_KEY_CHECKS = 1;
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for permission
-- ----------------------------
DROP TABLE IF EXISTS `permission`;
CREATE TABLE `permission` (
`p_id` int(11) NOT NULL AUTO_INCREMENT,
`perm_name` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
`perm_tag` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
PRIMARY KEY (`p_id`) USING BTREE,
UNIQUE INDEX `UKnojgqoyrg7x15s2qpu9x8tu5q`(`perm_name`) USING BTREE,
UNIQUE INDEX `UKqhboiosst3rok6m9scsef9f5x`(`perm_tag`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Compact;
-- ----------------------------
-- Records of permission
-- ----------------------------
INSERT INTO `permission` VALUES (1, '管理員用戶權限', 'ROLE_ADMIN');
INSERT INTO `permission` VALUES (2, '普通用戶權限', 'ROLE_COMMON');
SET FOREIGN_KEY_CHECKS = 1;
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for role_permission
-- ----------------------------
DROP TABLE IF EXISTS `role_permission`;
CREATE TABLE `role_permission` (
`r_id` int(11) NOT NULL,
`p_id` int(11) NOT NULL,
INDEX `FK92po1qlba53d8v5iuh13uhbsh`(`p_id`) USING BTREE,
INDEX `FKgdhemjxik42hbj22wao654453`(`r_id`) USING BTREE,
CONSTRAINT `FKgdhemjxik42hbj22wao654453` FOREIGN KEY (`r_id`) REFERENCES `role` (`r_id`) ON DELETE RESTRICT ON UPDATE RESTRICT,
CONSTRAINT `FK92po1qlba53d8v5iuh13uhbsh` FOREIGN KEY (`p_id`) REFERENCES `permission` (`p_id`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Compact;
-- ----------------------------
-- Records of role_permission
-- ----------------------------
INSERT INTO `role_permission` VALUES (1, 2);
INSERT INTO `role_permission` VALUES (2, 2);
INSERT INTO `role_permission` VALUES (2, 1);
SET FOREIGN_KEY_CHECKS = 1;
如此便成功使用了RBAC設計模型;
應用RBAC到security
要知道真正代表權限的是peimission的perm_tag字段,又用角色關聯權限,用戶又關聯角色來實現賦權;
前面我們只有管理員用戶並且擁有管理員角色,管理員角色又有普通用戶權限和管理員權限,現在我們添加一個普通用戶並賦予普通用戶角色使其擁有普通權限;
測試類:
@Test
void contextLoads() {
BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
User user = new User();
user.setUsername("common");
user.setPassword(bCryptPasswordEncoder.encode("common"));
user.setEmail("[email protected]");
user.setRealName("common");
List<Role> roles = new ArrayList<>();
Role role= new Role();
role.setR_id(1);
roles.add(role);
user.setRoleList(roles);
userRepository.save(user);
}
運行,然後成功;
現在我們有兩個用戶common、admin,分別綁定普通用戶角色跟管理員角色,而普通用戶角色綁定普通用戶權限,管理員角色綁定普通用戶權限以及管理員權限;
配置security配置類:void configure(HttpSecurity http)方法
@Override
protected void configure(HttpSecurity http) throws Exception {
//添加驗證碼過濾器
http.addFilterBefore(checkCodeAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
http
.authorizeRequests()
.antMatchers("/admin/**").hasAuthority("ROLE_ADMIN")//需要ROLE_ADMIN才能訪問
.antMatchers("/common/**").hasAuthority("ROLE_COMMON")//需要ROLE_COMMON才能訪問
.anyRequest().authenticated()
.and()
.formLogin()
.usernameParameter("username")//與登陸頁面一致
.passwordParameter("password")//與登陸頁面一致
.loginPage("/login.html")//自定義登陸頁面
.permitAll()//設置爲允許所有人訪問
.loginProcessingUrl("/securityLogin")//登陸處理請求路徑與action一致即可,不需要我們實現
.permitAll()//設置爲允許所有人訪問
.successHandler(myAuthenticationSuccessHandler)
.failureHandler(myAuthenticationFailureHandler)
.and()
.logout()//登出配置
.logoutUrl("/logout")//不需要我們實現,訪問這個自動登出
.invalidateHttpSession(true)
.deleteCookies("JSESSIONID")
.and()
.httpBasic()
.disable()
.csrf()
.disable();
}
找到之前的MyUserDetailService類,將其中註釋去掉;這步的目的就是將在登陸時,從數據庫找到用戶信息以及用戶的權限組封裝起來交給security去進行其他的認證操作;需要用戶類User實現UserDetails接口並定義List<GrantedAuthority> authorities屬性以及其get、set方法,security會根據這個屬性判斷該用戶擁有什麼權限
@Component
public class MyUserDetailService implements UserDetailsService {
@Autowired
UserRepository userRepository;
/**
* 根據用戶名獲取用戶信息
*
* @param s 用戶名
* @return User 返回一個用戶信息
* @throws UsernameNotFoundException
*/
@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
User user = userRepository.findByUsername(s);
System.out.println(user);
if (user != null) {
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
for (Role role : user.getRoleList()) {
for (Permission p : role.getPermissionList()) {
GrantedAuthority authority = new SimpleGrantedAuthority(p.getPermTag());
authorities.add(authority);
}
//將權限集合放進user
user.setAuthorities(authorities);
System.out.println("當前用戶是: " + user.getUsername());
}
}
return user;
}
}
提供一個controller類
@RestController
public class SecurityController {
@RequestMapping("/admin/hello")
public String adminHello() {
return "admin Hello World";
}
@RequestMapping("/common/hello")
public String commonHello() {
return "common Hello World";
}
}
ok,重啓項目,使用common登陸
登陸成功!分別訪問:
http://localhost:8080/security/common/hello //能訪問
http://localhost:8080/security/admin/hello //無法訪問,因爲admin下需要ROLE_ADMIN權限才能訪問
使用admin用戶登陸訪問看看:全部能訪問
運用成功