Spring?Security使用數(shù)據(jù)庫登錄認(rèn)證授權(quán)
一、搭建項目環(huán)境
1、創(chuàng)建 RBAC五張表
RBAC,即基于角色的權(quán)限訪問控制(Role-Based Access Control),就是用戶通過角色與權(quán)限進(jìn)行關(guān)聯(lián)。
在這種模型中,用戶與角色之間,角色與權(quán)限之間,一般者是多對多的關(guān)系。
在 MySQL數(shù)據(jù)庫中,創(chuàng)建如下幾個表:
DROP TABLE IF EXISTS sys_user;
CREATE TABLE sys_user(
id BIGINT NOT NULL AUTO_INCREMENT COMMENT '主鍵id' ,
username VARCHAR(60) COMMENT '用戶名' ,
password VARCHAR(255) COMMENT '密碼' ,
status tinyint(1) COMMENT '用戶狀態(tài),1-開啟-0禁用' ,
password_non_expired tinyint(1) COMMENT '密碼是否失效,1-可用,0-失效' ,
PRIMARY KEY (id)
) COMMENT = '用戶表';
DROP TABLE IF EXISTS sys_role;
CREATE TABLE sys_role(
id BIGINT NOT NULL AUTO_INCREMENT COMMENT '主鍵id' ,
role_name VARCHAR(64) NOT NULL COMMENT '角色名' ,
role_desc VARCHAR(64) NOT NULL COMMENT '角色描述' ,
PRIMARY KEY (id)
) COMMENT = '角色表';
DROP TABLE IF EXISTS sys_permission;
CREATE TABLE sys_permission(
id BIGINT NOT NULL AUTO_INCREMENT COMMENT '主鍵id' ,
parent_id BIGINT COMMENT '父id' ,
permission_name VARCHAR(64) COMMENT '菜單名稱' ,
permission_url VARCHAR(255) COMMENT '菜單地址' ,
PRIMARY KEY (id)
) COMMENT = '權(quán)限表';
DROP TABLE IF EXISTS sys_user_role;
CREATE TABLE sys_user_role(
id BIGINT NOT NULL AUTO_INCREMENT COMMENT '主鍵id' ,
user_id BIGINT NOT NULL COMMENT '用戶id' ,
role_id BIGINT COMMENT '角色id' ,
enabled tinyint(1) DEFAULT 1 COMMENT '是否有效' ,
PRIMARY KEY (id)
) COMMENT = '用戶角色關(guān)聯(lián)表';
DROP TABLE IF EXISTS sys_role_permission;
CREATE TABLE sys_role_permission(
id BIGINT NOT NULL AUTO_INCREMENT COMMENT '主鍵id' ,
role_id BIGINT NOT NULL COMMENT '角色id' ,
permission_id BIGINT COMMENT '權(quán)限id' ,
PRIMARY KEY (id)
) COMMENT = '角色權(quán)限表';

2、創(chuàng)建項目
創(chuàng)建 Mavne 項目,Springboot + Spring Security + MyBatis + MySQL + jsp。
1)配置文件如下
server:
port: 9090
# jsp配置
spring:
mvc:
view:
prefix: /pages/
suffix: .jsp
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/security_authority?useUnicode=true;characterEncoding=utf8;useSSL=true;serverTimezone=GMT
username: root
password: 123456
# mybatis配置
mybatis:
configuration:
map-underscore-to-camel-case: true
mapper-locations: classpath:mybatis/mapper/*.xml
logging:
level:
com.charge.learn.springsecurity.springboot.security.jsp.dao: debug
2)啟動類
@SpringBootApplication
@MapperScan("com.charge.learn.springsecurity.springboot.security.jsp.dao")
public class SpringSecurityApplication {
public static void main(String[] args) {
SpringApplication.run(SpringSecurityApplication.class, args);
}
}

二、整合 Spring Security實(shí)現(xiàn)用戶認(rèn)證
1、后端整合
1.1 用戶
Spring Security的用戶對象是 UserDetail類型,Spring Security在認(rèn)證流程中只認(rèn) UserDetail用戶。
- 通過
UserDetailsService的 loadUserByUsername方法獲取 UserDetail用戶。 - 認(rèn)證成功之后,調(diào)用的是這個帶有三個參數(shù)的 UsernamePasswordAuthenticationToken構(gòu)造方法,將 角色信息添加到了
ArrayList<GrantedAuthority>集合中。 - 在 successfulAuthentication 方法中,將認(rèn)證信息存儲到了SecurityContext中。

UserDetail接口的方法(根據(jù)用戶業(yè)務(wù)來處理這幾個值)。
- boolean enabled 賬戶是否可用
- boolean accountNonExpired 賬戶是否失效
- boolean credentialsNonExpired 賬戶密碼是否失效
- boolean accountNonLocked 賬戶是否鎖定
- Collection<? extends GrantedAuthority> getAuthorities() 獲取賬戶的所有權(quán)限(用戶角色)
注意:四個布爾類型的參數(shù)都為 true時,然后成功,否則,有一個為 false,就會認(rèn)證失敗。
所以,我們可以將我們的用戶封裝成 UserDetail對象。
這里我們讓用戶對象實(shí)現(xiàn) UserDetail接口,那么我們用戶就屬于 UserDetail類型的用戶,然后實(shí)現(xiàn)接口的方法。
public class SysUser implements UserDetails {
private Long id;
private String username;
private String password;
private Boolean status; //用戶狀態(tài),1-開啟-0禁用
private Boolean passwordNonExpired; //密碼是否失效,1-可用,0-失效
/**
* 用戶關(guān)聯(lián)的所有角色
*/
private List<SysRole> roles = new ArrayList<>();
//get、set方法
//標(biāo)記該字段不做json處理
@JsonIgnore
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return roles;
}
@JsonIgnore
@Override
public boolean isAccountNonExpired() {
return true;
}
@JsonIgnore
@Override
public boolean isAccountNonLocked() {
return true;
}
@JsonIgnore
@Override
public boolean isCredentialsNonExpired() {
return passwordNonExpired == null ? false : passwordNonExpired;
}
@JsonIgnore
@Override
public boolean isEnabled() {
return status == null ? false : status;
}
}
1.2 角色
Spring Security的權(quán)限對象是 GrantedAuthority類型。通過它的值來實(shí)現(xiàn)權(quán)限管理的。

所以,我們讓角色對象實(shí)現(xiàn)GrantedAuthority接口,那么我們角色就屬于 GrantedAuthority類型,然后實(shí)現(xiàn)接口的方法。
上面用戶可以直接將 角色添加到 Collection<? extends GrantedAuthority>集合中。
public class SysRole implements GrantedAuthority {
private Long id;
private String roleName;
private String roleDesc;
//get、set方法
//標(biāo)記該字段不做json處理
@JsonIgnore
@Override
public String getAuthority() {
return roleName;
}
}
1.3 SysUserService接受繼承UserDetailsService類
Spring Security在認(rèn)證流程中通過 UserDetailsService的 loadUserByUsername方法獲取 UserDetail用戶。

所以,我們讓 UserService接口繼承 UserDetailsService類,然后重寫 loadUserByUsername方法。
在 loadUserByUsername方法中,獲取我們的用戶信息( UserDetail類型),記得將用戶關(guān)聯(lián)的角色也賦值為用戶信息。
public interface SysUserService extends UserDetailsService {
void save(SysUser user);
}
@Service
@Transactional
public class SysUserServiceImpl implements SysUserService {
@Autowired
private SysUserMapper sysUserMapper;
@Autowired
private SysRoleMapper sysRoleMapper;
@Autowired
private BCryptPasswordEncoder passwordEncoder;
@Override
public void save(SysUser sysUser) {
// 將密碼加密入庫
sysUser.setPassword(passwordEncoder.encode(sysUser.getPassword()));
sysUserMapper.insert(sysUser);
}
/**
* 認(rèn)證業(yè)務(wù)
*
* @param username
* - 用戶在瀏覽器輸入的用戶名
* @return UserDetails - Spring Security的用戶對象,返回 null表示認(rèn)證失??!
* @throws UsernameNotFoundException
*/
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
/**
* 用戶信息和角色信息可以一步關(guān)聯(lián)查詢到位得到SysUser,我這里分開查詢
*/
// 1.查詢用戶
SysUser sysUser = sysUserMapper.getByUsername(username);
if (sysUser == null) {
return null;
}
// 2.獲取用戶關(guān)聯(lián)的所有角色
List<SysRole> sysRoles = sysRoleMapper.listAllByUserId(sysUser.getId());
sysUser.setRoles(sysRoles);
System.out.println("====> sysUser=" + sysUser.toString());
return sysUser;
}
}
mapper.xml中的幾個方法
<select id="getByUsername" resultMap="BaseResultMap">
select id, username, password, status, password_non_expired from sys_user where username = #{username}
</select>
<select id="listAllByUserId" resultMap="BaseResultMap">
SELECT r.id, r.role_name role_name, r.role_desc role_desc
FROM sys_role r, sys_user_role ur
WHERE r.id = ur.role_id AND ur.user_id = #{userId}
</select>
1.4 創(chuàng)建 SpringSecurity配置類
自定義一個配置類,添加@EnableWebSecurity注解,并繼承WebSecurityConfigurerAdapter類。然后就擁有了 SpringSecutiry的所有默認(rèn)配置。我們也可以修改配置。
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private SysUserService userService;
// 加密對象注入IOC容器
@Bean
public BCryptPasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
// 1.指定認(rèn)證對象的來源(內(nèi)存或者數(shù)據(jù)庫),指定加密方式
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService).passwordEncoder(passwordEncoder());
}
//2. SpringSecurity配置相關(guān)信息
@Override
public void configure(HttpSecurity http) throws Exception {
// 釋放靜態(tài)資源,指定攔截規(guī)則,指定自定義的認(rèn)證和退出頁面,csrf配置等
http.authorizeRequests()
// 指定攔截規(guī)則
.antMatchers("/login.jsp", "failer.jsp", "/css/**", "/img/**", "/plugins/**").permitAll() //釋放這些資源,不攔截
.antMatchers("/**").hasAnyRole("USER", "ADMIN") //所有資源都需要這些角色中的一個
.anyRequest().authenticated() //其他請求,必須認(rèn)證通過之后才能訪問
.and() // 表示新的一個配置開始
// 指定自定義的認(rèn)證頁面
.formLogin()
.loginPage("/login.jsp")
.loginProcessingUrl("/login")
.successForwardUrl("/index.jsp")
.failureForwardUrl("/failer.jsp")
.permitAll() // 釋放這些資源,不攔截登錄
.and()
// 指定自定義的退出頁面
.logout()
.logoutSuccessUrl("/logout")
.invalidateHttpSession(true) // 清楚session
.logoutSuccessUrl("/login.jsp")
.permitAll()
//.and()
// 禁用csrf配置,默認(rèn)開啟的(一般不寫,頁面要加csrf),這里我們測試下
// .and()
// .csrf()
// .disable()
;
}
主要配置信息如下:
- 指定認(rèn)證對象 SysUserService (UserDetailsService類型)
- 指定了用戶密碼使用的加密對象
- SpringSecurity配置相關(guān)信息,比如:指定攔截規(guī)則,指定自定義的認(rèn)證頁面,csrf等。
2、前端整合
在 Spring Security 中,如果我們不做任何配置,默認(rèn)的登錄頁面和登錄接口的地址都是 /login,即默認(rèn)會存在如下兩個請求:
- GET http://localhost:8080/login
- POST http://localhost:8080/login
如果是 GET 請求表示你想訪問登錄頁面,如果是 POST 請求,表示你想提交登錄數(shù)據(jù)。默認(rèn)的表單字段為 username和password。
SpringSecurity 默認(rèn) 是開啟 csrf防護(hù)機(jī)制。
所以,在自定義的表單上添加上 _csrf隱藏input(必須要寫在form表單里面)。
引入 security標(biāo)簽庫 <%@taglib uri="http://www.springframework.org/security/tags" prefix="security"%> <security:csrfInput/>
啟動項目,登錄認(rèn)證訪問ok.
三、整合 Spring Security實(shí)現(xiàn)用戶授權(quán)
認(rèn)證過程獲取用戶信息時,我們已經(jīng)把用戶關(guān)聯(lián)的角色信息設(shè)置到了 UserDetails中,所以,我們只需要分配 資源訪問的角色就可以了。
1、后端
1.1 開啟 Spring Security權(quán)限控制
Spring Security可以通過注解的方式來控制類或者方法的訪問權(quán)限。支持開啟權(quán)限控制的注解類型如下:
- jsr250-annotations:表示支持 jsr250-api的注解
- pre-post-annotations:表示支持 spring表達(dá)式注解
- secured-annotations:這才是 Spring Security提供的注解

在實(shí)際開發(fā)中,用一類即可,三個都開啟也沒關(guān)系。
在 SpringSecurity配置類上 添加 @EnableGlobalMethodSecurity注解,表示開啟 Spring Security權(quán)限控制,這里我們?nèi)惗奸_啟了。
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled=true, prePostEnabled = true, jsr250Enabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
...
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled=true, prePostEnabled = true, jsr250Enabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
...1.2 在角色對應(yīng)類或者方法上添加權(quán)限注解
1.2.1 使用 Spring Security注解
- @Secured({“ROLE_ADMIN”,“ROLE_PRODUCT”})
@Controller
@RequestMapping("/user")
@Secured("ROLE_ADMIN") //表示當(dāng)前類中所有方法需要 ROLE_ADMIN才能訪問
public class UserController {
@Autowired
private UserService userService;
@RequestMapping("/findAll")
public String findAll(Model model){
List<SysUser> list = userService.findAll();
model.addAttribute("list", list);
return "user-list";
}
。。。
}
1.2.2 使用 Spring表達(dá)式注解
- @PreAuthorize(“hasAnyRole(‘ROLE_ADMIN’,‘ROLE_PRODUCT’)”)
@Controller
@RequestMapping("/product")
public class ProductController {
@RequestMapping("/findAll")
//表示當(dāng)前類中findAll方法需要 ROLE_ADMIN或者 ROLE_PRODUCT才能訪問
@PreAuthorize("hasAnyRole('ROLE_ADMIN','ROLE_PRODUCT')")
public String findAll(){
return "product-list";
}
}
1.2.3 使用 JSR-250注解
- @RolesAllowed({“ROLE_ADMIN”,“ROLE_User”})
@Controller
@RequestMapping("/order")
@RolesAllowed({"ROLE_ADMIN","ROLE_USER"}) //表示當(dāng)前類中所有方法都需要ROLE_ADMIN或者ROLE_User才能訪問
public class OrderController {
@RequestMapping("/findAll")
public String findAll(){
return "order-list";
}
}
2、前端
在jsp業(yè)頁面中,對每個菜單資源通過 SpringSecurity標(biāo)簽庫指定訪問所需的角色。
<%@taglib uri="http://www.springframework.org/security/tags" prefix="security" %>
<!-- 指定訪問所需角色 -->
<security:authorize access="hasAnyRole('ROLE_ADMIN', '等等')">
啟動項目,通過不同的用戶登錄,授權(quán)訪問ok.
到此這篇關(guān)于Spring Security使用數(shù)據(jù)庫登錄認(rèn)證授權(quán)的文章就介紹到這了,更多相關(guān)Spring Security數(shù)據(jù)庫登錄認(rèn)證授權(quán)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- SpringBoot+SpringSecurity+JWT實(shí)現(xiàn)系統(tǒng)認(rèn)證與授權(quán)示例
- mall整合SpringSecurity及JWT認(rèn)證授權(quán)實(shí)戰(zhàn)下
- mall整合SpringSecurity及JWT實(shí)現(xiàn)認(rèn)證授權(quán)實(shí)戰(zhàn)
- Spring Security+JWT實(shí)現(xiàn)認(rèn)證與授權(quán)的實(shí)現(xiàn)
- Java Spring Security認(rèn)證與授權(quán)及注銷和權(quán)限控制篇綜合解析
- SpringSecurity數(shù)據(jù)庫進(jìn)行認(rèn)證和授權(quán)的使用
- SpringBoot+SpringSecurity實(shí)現(xiàn)基于真實(shí)數(shù)據(jù)的授權(quán)認(rèn)證
- Spring Security OAuth2認(rèn)證授權(quán)示例詳解
- Spring Security實(shí)現(xiàn)身份認(rèn)證和授權(quán)的示例代碼
相關(guān)文章
Mybatis如何自動生成數(shù)據(jù)庫表結(jié)構(gòu)總結(jié)
這篇文章主要給大家介紹了關(guān)于Mybatis如何自動生成數(shù)據(jù)庫表結(jié)構(gòu)的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者使用Mybatis具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2019-11-11
介紹Java的大數(shù)類(BigDecimal)和八種舍入模式
在實(shí)際應(yīng)用中,需要對更大或者更小的數(shù)進(jìn)行運(yùn)算和處理。Java在java.math包中提供的API類BigDecimal,用來對超過16位有效位的數(shù)進(jìn)行精確的運(yùn)算。本文將介紹Java中的大數(shù)類BigDecimal及其八種舍入模式,有需要的可以參考借鑒。2016-08-08
Java多線程中wait?notify等待喚醒機(jī)制詳解
這篇文章主要介紹了Java多線程中wait?notify等待喚醒機(jī)制,由于線程之間是搶占式執(zhí)行的,因此線程的執(zhí)行順序難以預(yù)知,但是實(shí)際開發(fā)中有時候我們希望合理的協(xié)調(diào)多個線程之間的執(zhí)行先后順序,所以這里我們來介紹下等待喚醒機(jī)制,需要的朋友可以參考下2024-10-10
Java實(shí)現(xiàn)掃雷游戲詳細(xì)代碼講解
windows自帶的游戲《掃雷》是陪伴了無數(shù)人的經(jīng)典游戲,本文將利用Java語言實(shí)現(xiàn)這一經(jīng)典的游戲,文中的示例代碼講解詳細(xì),感興趣的可以學(xué)習(xí)一下2022-05-05
java實(shí)現(xiàn)的海盜算法優(yōu)化版
這篇文章主要介紹了java實(shí)現(xiàn)的海盜算法優(yōu)化版,結(jié)合實(shí)例形式分析了java海盜算法的具體實(shí)現(xiàn)技巧,需要的朋友可以參考下2017-07-07
Java?中的整型數(shù)據(jù)類型((byte,?short,?int,?long?)的取值范圍及使用不同的整型的場景
Java中的整型數(shù)據(jù)類型包括byte、short、int和long,每種類型都有不同的內(nèi)存占用和取值范圍,選擇合適的整型類型取決于具體需求,如內(nèi)存敏感場景、性能考量和避免整數(shù)溢出,示例代碼展示了如何聲明和初始化不同整型變量,并打印它們的最大值,感興趣的朋友一起看看吧2025-03-03

