SpringSecurity實(shí)現(xiàn)自定義數(shù)據(jù)源實(shí)戰(zhàn)指南
Spring Security簡(jiǎn)介
Java中的安全框架通常是指解決Web應(yīng)用安全問(wèn)題的框架。自行實(shí)現(xiàn)Web應(yīng)用的安全性并不容易,需要考慮不同的認(rèn)證和授權(quán)機(jī)制、網(wǎng)絡(luò)關(guān)鍵數(shù)據(jù)傳輸加密等多方面的問(wèn)題。
Java中常用的安全框架有Spring Security和Shiro,這兩個(gè)安全框架都提供了強(qiáng)大功能,可以很容易實(shí)現(xiàn)Web應(yīng)用的很多安全防護(hù)。下面對(duì)這兩個(gè)安全框架的特點(diǎn)進(jìn)行講解
Spring Security是Spring生態(tài)系統(tǒng)中重要的一員,是一個(gè)基于AOP思想和Servlet過(guò)濾器實(shí)現(xiàn)的安全框架,它提供了完善的認(rèn)證機(jī)制和方法級(jí)的授權(quán)功能,是一款非常優(yōu)秀的權(quán)限管理框架。
在進(jìn)行安全管理的過(guò)程中都涉及權(quán)限管理的兩個(gè)重要概念:Authentication(認(rèn)證)
和Authorization(授權(quán))。權(quán)限管理是指根據(jù)系統(tǒng)設(shè)置的安全規(guī)則或者安全策略,用戶可以訪問(wèn)且只能訪問(wèn)自己被授權(quán)的資源。
實(shí)現(xiàn)權(quán)限管理通常需要三個(gè)對(duì)象,分別為用戶、角色、權(quán)限,這三個(gè)對(duì)象的說(shuō)明如下。
- 用戶:主要包含用戶名、密碼和當(dāng)前用戶的角色信息,可以實(shí)現(xiàn)認(rèn)證操作。
- 角色:主要包含角色名稱、角色描述和當(dāng)前角色擁有的權(quán)限信息,可以實(shí)現(xiàn)授權(quán)操作。
- 權(quán)限:權(quán)限也可以稱為菜單,主要包含當(dāng)前權(quán)限名稱、url地址等信息,可以實(shí)現(xiàn)動(dòng)態(tài)展示菜單。
一、SQL數(shù)據(jù)表的設(shè)計(jì)
-- 創(chuàng)建用戶表
CREATE TABLE `user` (
-- 主鍵id,自增
`id` INT(11) NOT NULL AUTO_INCREMENT COMMENT '用戶id',
-- 用戶名字段
`username` VARCHAR(32) DEFAULT NULL COMMENT '用戶名',
-- 密碼字段(建議存儲(chǔ)加密后的密碼)
`password` VARCHAR(255) DEFAULT NULL COMMENT '密碼',
-- 賬戶啟用狀態(tài):1=啟用,0=禁用
`enabled` TINYINT(1) DEFAULT NULL COMMENT '是否可用',
-- 賬戶未過(guò)期標(biāo)志:1=未過(guò)期,0=已過(guò)期
`accountNonExpired` TINYINT(1) DEFAULT NULL COMMENT '賬戶過(guò)期',
-- 賬戶未鎖定標(biāo)志:1=未鎖定,0=已鎖定
`accountNonLocked` TINYINT(1) DEFAULT NULL COMMENT '賬戶鎖定',
-- 憑證未過(guò)期標(biāo)志:1=未過(guò)期,0=已過(guò)期
`credentialsNonExpired` TINYINT(1) DEFAULT NULL COMMENT '憑證過(guò)期',
-- 設(shè)置主鍵
PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8;
-- 創(chuàng)建角色表(使用roles避免與MySQL關(guān)鍵字沖突)
CREATE TABLE `roles` (
-- 主鍵id,自增
`id` INT(11) NOT NULL AUTO_INCREMENT,
-- 角色英文名,如ROLE_ADMIN
`name` VARCHAR(32) DEFAULT NULL,
-- 角色中文名,如管理員
`name_zh` VARCHAR(32) DEFAULT NULL,
-- 設(shè)置主鍵
PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8;
-- 創(chuàng)建用戶角色關(guān)聯(lián)表(多對(duì)多關(guān)系)
CREATE TABLE `user_role` (
-- 主鍵id,自增
`id` INT(11) NOT NULL AUTO_INCREMENT,
-- 用戶id,關(guān)聯(lián)user表
`uid` INT(11) DEFAULT NULL,
-- 角色id,關(guān)聯(lián)roles表
`rid` INT(11) DEFAULT NULL,
-- 設(shè)置主鍵
PRIMARY KEY (`id`),
-- 為uid字段創(chuàng)建索引,提高查詢效率
KEY `uid` (`uid`),
-- 為rid字段創(chuàng)建索引,提高查詢效率
KEY `rid` (`rid`),
-- 外鍵約束:uid引用user表的id字段
CONSTRAINT `user_role_ibfk_1` FOREIGN KEY (`uid`) REFERENCES `user` (`id`),
-- 外鍵約束:rid引用roles表的id字段
CONSTRAINT `user_role_ibfk_2` FOREIGN KEY (`rid`) REFERENCES `roles` (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8;
-- ==================== 第一步:插入用戶數(shù)據(jù) ====================
-- 插入3個(gè)測(cè)試用戶
INSERT INTO `user` (
`username`,
`password`,
`enabled`,
`accountNonExpired`,
`accountNonLocked`,
`credentialsNonExpired`
) VALUES
-- 管理員用戶:全部狀態(tài)正常
('admin', 'admin123', 1, 1, 1, 1),
-- 普通用戶:正常可用
('zhangsan', '123456', 1, 1, 1, 1),
-- 被禁用的用戶:用于測(cè)試禁用功能
('lisi', '123456', 0, 1, 1, 1);
-- ==================== 第二步:插入角色數(shù)據(jù) ====================
-- 插入幾個(gè)常用角色
INSERT INTO `roles` (`name`, `name_zh`) VALUES
('ROLE_ADMIN', '管理員'),
('ROLE_USER', '普通用戶'),
('ROLE_GUEST', '訪客');
-- ==================== 第三步:插入用戶角色關(guān)聯(lián) ====================
-- 給用戶分配角色
INSERT INTO `user_role` (`uid`, `rid`) VALUES
-- admin用戶:同時(shí)擁有管理員和普通用戶角色
(1, 1), -- admin -> ROLE_ADMIN
(1, 2), -- admin -> ROLE_USER
-- zhangsan用戶:只有普通用戶角色
(2, 2), -- zhangsan -> ROLE_USER
-- lisi用戶:只有訪客角色
(3, 3); -- lisi -> ROLE_GUEST二、引入Maven依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>三、配置yml文件
spring:
application:
name: SpringSecurity
datasource:
url: jdbc:mysql://localhost:3306/security?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
username: root
password: password
driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:
mapper-locations: com.example.springsecurity.mapper/**/*.xml四、編寫實(shí)體類、mapper.xml、mapper層
package com.example.springsecurity.entity;
import lombok.*;
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Roles {
private Integer id;
private String name;
private String nameZh;
}package com.example.springsecurity.entity;
import lombok.ToString;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.*;
@ToString
// 自定義用戶User
public class User implements UserDetails {/*這里繼承UserDetails是因?yàn)镾pringSecurity低層只認(rèn)這個(gè),相當(dāng)于不良人只認(rèn)大帥不認(rèn)令牌*/
private Integer id; // 用戶id
private String username; // 用戶名
private String password; // 密碼
private boolean enabled; // 是否可用
private boolean accountNonExpired; // 賬戶過(guò)期
private boolean accountNonLocked; // 賬戶鎖定
private boolean credentialsNonExpired; // 憑證過(guò)期
private List<Roles> roles = new ArrayList<>(); // 用戶角色信息
// 返回權(quán)限信息
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
Set<SimpleGrantedAuthority> authorities = new HashSet<>();
roles.forEach(role -> {
SimpleGrantedAuthority simpleGrantedAuthority = new SimpleGrantedAuthority(role.getName());
authorities.add(simpleGrantedAuthority);
});
return authorities;
}
@Override
public String getPassword() {
return password;
}
@Override
public String getUsername() {
return username;
}
@Override
public boolean isAccountNonExpired() {
return accountNonExpired;
}
@Override
public boolean isAccountNonLocked() {
return accountNonLocked;
}
@Override
public boolean isCredentialsNonExpired() {
return credentialsNonExpired;
}
@Override
public boolean isEnabled() {
return enabled;
}
public void setId(Integer id) {
this.id = id;
}
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public void setAccountNonExpired(boolean accountNonExpired) {
this.accountNonExpired = accountNonExpired;
}
public void setAccountNonLocked(boolean accountNonLocked) {
this.accountNonLocked = accountNonLocked;
}
public void setCredentialsNonExpired(boolean credentialsNonExpired) {
this.credentialsNonExpired = credentialsNonExpired;
}
public void setRoles(List<Roles> roles) {
this.roles = roles;
}
public Integer getId() {
return id;
}
public List<Roles> getRoles() {
return roles;
}
}<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.springsecurity.mapper.RolesMapper">
<select id="queryRolesByName" resultType="com.example.springsecurity.entity.Roles">
select
r.id,
r.name,
r.name_zh
from
roles r,user_role `ur`
where
r.id = `ur`.rid
and
ur.uid = #{uid}
</select>
</mapper><?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.springsecurity.mapper.UserMapper">
<select id="queryUserByName" resultType="com.example.springsecurity.entity.User">
select * from user <if test="username !=null and username !=''">
<where>
username = #{username}
</where>
limit 1
</if>
</select>
</mapper>public interface RolesMapper {
List<Roles> queryRolesByName(@Param("uid") Integer uid);
}public interface UserMapper {
User queryUserByName(@Param("username") String username);
}五、編寫service層
@Service
@RequiredArgsConstructor//lombok自動(dòng)生成構(gòu)造函數(shù)
public class MyDetailsService implements UserDetailsService {
private final UserMapper userMapper;
private final RolesMapper rolesMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
System.out.println("username:========"+username);
User user = userMapper.queryUserByName(username);
if(ObjectUtil.isEmpty(user))
{
throw new UsernameNotFoundException("用戶不存在");
}
//這里因?yàn)闆](méi)有配置密碼格式,后臺(tái)數(shù)據(jù)庫(kù)如果是明文的話,就需要用這個(gè)暫時(shí)“過(guò)渡”一下
user.setPassword("{noop}" + user.getPassword());
List<Roles> roles = rolesMapper.queryRolesByName(user.getId());
user.setRoles(roles);
return user;
}
}六、測(cè)試運(yùn)行
到此這篇關(guān)于SpringSecurity實(shí)現(xiàn)自定義數(shù)據(jù)源的文章就介紹到這了,更多相關(guān)SpringSecurity自定義數(shù)據(jù)源內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Spring?Security配置多個(gè)數(shù)據(jù)源并添加登錄驗(yàn)證碼的實(shí)例代碼
- spring Security配置攔截規(guī)則小結(jié)
- springSecurity使用實(shí)戰(zhàn)指南
- JAVA中Spring Security示例及常見問(wèn)題
- Java中Spring Security的使用及最佳實(shí)踐
- Spring Security介紹及配置實(shí)現(xiàn)代碼
- vue3整合SpringSecurity加JWT實(shí)現(xiàn)登錄認(rèn)證
- SpringSecurity Oauth2訪問(wèn)令牌續(xù)期問(wèn)題
相關(guān)文章
已有的springcloud+mybatis項(xiàng)目升級(jí)為mybatis-plus的方法
這篇文章主要介紹了已有的springcloud+mybatis項(xiàng)目升級(jí)為mybatis-plus,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03
Java實(shí)現(xiàn)前端jsencrypt.js加密后端解密的示例代碼
這篇文章主要為大家詳細(xì)介紹了如何利用jsencrypt.js實(shí)現(xiàn)前端加密,利用Java實(shí)現(xiàn)后端解密的功能,文中的示例代碼講解詳細(xì),需要的可以參考一下2022-09-09
Java使用POI-TL實(shí)現(xiàn)生成有個(gè)性的簡(jiǎn)歷
POI-TL?是一個(gè)基于?Apache?POI?的?Java?庫(kù),專注于在?Microsoft?Word?文檔(.docx?格式)中進(jìn)行模板填充和動(dòng)態(tài)內(nèi)容生成,下面我們看看如何使用POI-TL生成有個(gè)性的簡(jiǎn)歷吧2024-11-11
淺析Java?NIO?直接緩沖區(qū)和非直接緩沖區(qū)
本篇文章主要為大家介紹了Java?NIO?中直接緩沖區(qū)和非直接緩沖區(qū)的定義以及使用流程,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-11-11
IDEA 集成log4j將SQL語(yǔ)句打印在控制臺(tái)上的實(shí)現(xiàn)操作
這篇文章主要介紹了IDEA 集成log4j將SQL語(yǔ)句打印在控制臺(tái)上的實(shí)現(xiàn)操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-02-02
Mybatis 的 Interceptor(攔截器) 與 JSqlparser&
MyBatis插件提供了在SQL執(zhí)行前進(jìn)行攔截、擴(kuò)展的功能,允許用戶自定義邏輯處理,本文介紹Mybatis 的 Interceptor(攔截器) 與 JSqlparser 結(jié)合解析SQL 使SpringBoot項(xiàng)目多數(shù)據(jù)庫(kù)兼容的嘗試(推薦),感興趣的朋友跟隨小編一起看看吧2026-01-01
Dubbo無(wú)法訪問(wèn)遠(yuǎn)程Zookeeper已注冊(cè)服務(wù)的問(wèn)題解決方案
今天小編就為大家分享一篇關(guān)于Dubbo無(wú)法訪問(wèn)遠(yuǎn)程Zookeeper已注冊(cè)服務(wù)的問(wèn)題解決方案,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2019-03-03
Spring組件實(shí)例化擴(kuò)展點(diǎn)之InstantiationAwareBeanPostProcessor使用場(chǎng)景解析
InstantiationAwareBeanPostProcessor是Spring 生命周期中的一個(gè)關(guān)鍵擴(kuò)展接口,繼承自 BeanPostProcessor,這篇文章主要介紹了Spring組件實(shí)例化擴(kuò)展點(diǎn)之InstantiationAwareBeanPostProcessor使用場(chǎng)景解析,需要的朋友可以參考下2025-06-06

