SpringSecurity結合數據表實現權限認證:
下面的案例是在SpringBoot框架實現的:
步驟一:准備數據庫表 以下是五張表的腳本
### 用戶表
create table Sys_User
(
id int primary key auto_increment,
username varchar(32),
password varchar(32)
);
###角色表
create table Sys_Role
(
id int primary key auto_increment,
name varchar(32)
);
###權限表
create table Sys_Permission
(
id int primary key auto_increment,
name varchar(32),
description varchar(256),
url varchar(256),
pid int
);
###用戶角色表
create table Sys_user_role
(
id int primary key auto_increment,
sys_user_id int,
sys_role_id int
);
###角色權限表
create table Sys_role_permission
(
id int primary key auto_increment,
role_id int,
permission_id int
);
insert into SYS_USER(id,username,password) values(1,'admin','admin');
insert into SYS_USER(id,username,password) values(2,'abel','abel');
insert into SYS_ROLE(id,name) values(1,'ROLE_ADMIN');
insert into SYS_ROLE(id,name) values(2,'ROLE_USER');
insert into SYS_USER_ROLE(SYS_USER_ID,SYS_ROLE_ID) values(1,1);
insert into SYS_USER_ROLE(SYS_USER_ID,SYS_ROLE_ID) values(2,2);
BEGIN;
INSERT INTO `Sys_permission` VALUES('1','ROLE_HOME','home','/',null),('2','ROLE_ADMIN','ABel','admin',null);
COMMIT;
BEGIN;
INSERT INTO `Sys_role_permission` VALUES('1','1','1'),('2','1','2'),('3','2','1');
COMMIT;
步驟二:
//1.1 application的配置
spring.datasource.url=jdbc:mysql://localhost:3306/y2156
spring.datasource.username=root
spring.datasource.password=55163
spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.datasource.max-active = 20
spring.datasource.max-idle = 8
spring.datasource.min-idle=8
spring.datasource.initial-sixe=10
######Spring jpa的配置信息
spring.jpa.database=MYSQL
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=update
spring.jpa.hibernate.naming-strategy=org.hibernate.cfg.ImprovedNamingStrategy
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
server.port=8080
cn.happy.name = weilengdeyu
###es的相關配置
spring.data.elasticsearch.cluster-nodes=localhost:9300
spring.data.elasticsearch.properties.transport.tcp.connect_timeout=5s
##模板引擎的配置
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.cache = false
spring.thymeleaf.mode=HTML5
###讓配置文件找到mapping下的小配置文件
mybatis.mapper-locations=classpath:mapping/*.xml
###別名設置
mybatis.type-aliases-package=cn.happy.entity
//1.2 在pom.xml中引入依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!--thymeleaf和springsecurity整合的依賴-->
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity4</artifactId>
</dependency>
<!--thymeleaf 新的模板引擎,比jsp要出色-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
步驟三:准備ui層
// 1.1. login.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>登錄</title>
<script type="text/javascript" th:src="@{/js/jquery-3.3.1.js}"></script>
<script type="text/javascript">
</script>
</head>
<body>
<div>
<form th:action="@{/login}" method="post">
<h2>請登錄</h2>
用戶名:<input name="username" type="text"/><br/>
密碼:<input name="password" type="password"/><br/>
<input type="submit" value="登錄"/><br/>
<div th:if="${loginError}"></div>
<div th:text="${errorMsg}"></div>
</form>
</div>
</body>
</html>
//1.2 index.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4">
<head>
<meta charset="UTF-8">
<title>博客系統</title>
<script type="text/javascript" th:src="@{/js/jquery-3.3.1.js}"></script>
<script type="text/javascript">
</script>
</head>
<body>
<div>
<!--authorize:認證,授權-->
<div sec:authorize="isAuthenticated()">
<p>登錄的用戶名為:<span sec:authentication="name"></span></p>
<p>登錄的權限為:<span sec:authentication="principal.authorities"></span></p>
</div>
</div>
</body>
</html>
步驟四: 在entity層創建實體類
// 1.1 SysUser類
public class SysUser {
private Integer id;
private String username;
private String password;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
//1.2. Permission 類
public class Permission {
private int id;
//權限名稱
private String name;
//權限描述
private String description;
//授權鏈接
private String url;
//父節點id
private int pid;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public int getPid() {
return pid;
}
public void setPid(int pid) {
this.pid = pid;
}
}
步驟五:
在dao層創建需要的接口
//1.1 創建IUserDAO
import cn.happy.entity.SysUser;
public interface IUserDAO {
//01.根據用戶名稱檢索用戶對象 Spring Security
public SysUser loadUserByUsername(String username);
}
//1.2 創建IPermissionDAO
import cn.happy.entity.Permission;
import java.util.List;
public interface IPermissionDAO {
//01.查詢所有權限集合 看用戶能不能訪問某個資源
public List<Permission> findAll();
//02.根據用戶編號查詢權限集合
public List<Permission> findByAdminUserId(int userId);
}
步驟六:
注意: service層不用寫接口,因為SpringSecurity框架已經幫我們封裝好了相關的方法
所以我們可以直接研發實現類即可:
import cn.happy.dao.IPermissionDAO;
import cn.happy.dao.IUserDAO;
import cn.happy.entity.Permission;
import cn.happy.entity.SysUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
//用戶詳情服務
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import java.util.ArrayList;
import java.util.List;
public class UserServiceImpl implements UserDetailsService {
//在service層需要植入DAO的接口
@Autowired
private IUserDAO userDAO;
@Autowired
private IPermissionDAO permissionDAO;
@Override
public UserDetails loadUserByUsername(String username){
//1.調度的是我自己在DAO層定制的方法,只不過有一個巧合,就是我自己定制的方法和spring security中的方法名稱一致。
SysUser sysUser = userDAO.loadUserByUsername(username);
//斷定
if(sysUser!=null){ //有用戶對象
//根據用戶對象中的用戶編號,獲取該用戶所擁有的所有權限集合
List<Permission> permissions=permissionDAO.findByAdminUserId(sysUser.getId());
//如果想讓你的權限在UI層可以使用,必須進行包裝,保證類型是GrantedAuthority
//1.先准備一個和框架對接的容器
List<GrantedAuthority> grantedAuthorities=new ArrayList<>();
//2.進行容器的替換
for(Permission permission:permissions){
//每個permission對象都是一個權限對象
if(permission != null && permission.getName()!= null){ //進行了雙重校驗,保證權限的准確性
GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(permission.getName());
//將單個grantedAuthority對象添加到集合中
grantedAuthorities.add(grantedAuthority);
}
}
return new User(sysUser.getUsername(),sysUser.getPassword(),grantedAuthorities);
}else{
throw new UsernameNotFoundException("admin:"+username+"do not exits");
}
}
}
步驟七:
// 1.1.創建WebSecurityConfig 繼承了 WebSecurityConfigurerAdapter
import cn.happy.service.impl.UserServiceImpl;
import cn.happy.util.MyPasswordEncoder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated() //任何請求,登錄后可以訪問
.and().formLogin() //基於表單的登錄
.loginPage("/login") //登錄頁面
.failureUrl("/login-error") //錯誤頁面
.permitAll() //登錄和錯誤頁面用戶可以任意訪問
.and()
.logout().permitAll();//注銷行為可以任意訪問
}
@Bean
//UserDetailsService是一個接口
//意思是@Bean明確的指示了一種方法,什么方法呢----產生一個bean的方法,並且將bean實例交給spring容器管理
UserDetailsService customUserService(){
//向spring容器直接植入了一個Service
return new UserServiceImpl();
}
//定制了一個configuration
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {//認證管理構建器
auth.userDetailsService(customUserService()).passwordEncoder(new MyPasswordEncoder()); //user Details Service驗證
}
}
//1.2.上面的程序中需要傳入一個MyPasswordEncoder()參數
那么我們就來創建一個自定義的密碼編輯器
import org.springframework.security.crypto.password.PasswordEncoder;
public class MyPasswordEncoder implements PasswordEncoder {
@Override
public String encode(CharSequence arg0) {
return arg0.toString();
}
@Override
public boolean matches(CharSequence arg0, String arg1) {
return arg1.equals(arg0.toString());
}
}
步驟八:
創建controller,實現一系列的調度和訪問
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class MainController {
@GetMapping("/index")
public String index(){
return "index";
}
@GetMapping("/login")
public String login(){
return "login";
}
@GetMapping("/login-error")
public String loginError(Model model){
model.addAttribute("loginError",true);
model.addAttribute("errorMsg","登錄失敗,用戶名或密碼錯誤");
return "login";
}
}