Springboot整合SpringSecurity的完整案例詳解
一.Spring Security介紹
Spring Security是基于Spring生態(tài)圈的,用于提供安全訪問控制解決方案的框架。Spring Security的安 全管理有兩個(gè)重要概念,分別是Authentication(認(rèn)證)和Authorization(授權(quán))。 為了方便Spring Boot項(xiàng)目的安全管理,Spring Boot對(duì)Spring Security安全框架進(jìn)行了整合支持,并提 供了通用的自動(dòng)化配置,從而實(shí)現(xiàn)了Spring Security安全框架中包含的多數(shù)安全管理功能。
Spring Security登錄認(rèn)證主要涉及兩個(gè)重要的接口 UserDetailService和UserDetails接口。
UserDetailService接口主要定義了一個(gè)方法 loadUserByUsername(String username)用于完成用戶信息的查 詢,其中username就是登錄時(shí)的登錄名稱,登錄認(rèn)證時(shí),需要自定義一個(gè)實(shí)現(xiàn)類實(shí)現(xiàn)UserDetailService接 口,完成數(shù)據(jù)庫查詢,該接口返回UserDetail。
UserDetail主要用于封裝認(rèn)證成功時(shí)的用戶信息,即UserDetailService返回的用戶信息,可以用Spring
自己的User對(duì)象,但是最好是實(shí)現(xiàn)UserDetail接口,自定義用戶對(duì)象。
二.Spring Security認(rèn)證步驟
1. 自定UserDetails類:當(dāng)實(shí)體對(duì)象字段不滿足時(shí)需要自定義UserDetails,一般都要自定義
UserDetails。
2. 自定義UserDetailsService類,主要用于從數(shù)據(jù)庫查詢用戶信息。
3. 創(chuàng)建登錄認(rèn)證成功處理器,認(rèn)證成功后需要返回JSON數(shù)據(jù),菜單權(quán)限等。
4. 創(chuàng)建登錄認(rèn)證失敗處理器,認(rèn)證失敗需要返回JSON數(shù)據(jù),給前端判斷。
5. 創(chuàng)建匿名用戶訪問無權(quán)限資源時(shí)處理器,匿名用戶訪問時(shí),需要提示JSON。
6. 創(chuàng)建認(rèn)證過的用戶訪問無權(quán)限資源時(shí)的處理器,無權(quán)限訪問時(shí),需要提示JSON。
7. 配置Spring Security配置類,把上面自定義的處理器交給Spring Security。
三.Spring Security認(rèn)證實(shí)現(xiàn)
3.1添加Spring Security依賴
在pom.xml文件中添加Spring Security核心依賴,代碼如下所
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>3.2自定義UserDetails
當(dāng)實(shí)體對(duì)象字段不滿足時(shí)Spring Security認(rèn)證時(shí),需要自定義UserDetails。
1. 將User類實(shí)現(xiàn)UserDetails接口
2. 將原有的isAccountNonExpired、isAccountNonLocked、isCredentialsNonExpired和isEnabled屬性修 改成boolean類型,同時(shí)添加authorities屬性。
@Data
@TableName("sys_user")
public class User implements Serializable, UserDetails {
//省略原有的屬性......
/**
* 帳戶是否過期(1 未過期,0已過期)
*/
private boolean isAccountNonExpired = true;
/**
* 帳戶是否被鎖定(1 未過期,0已過期)
*/
private boolean isAccountNonLocked = true;
/**
* 密碼是否過期(1 未過期,0已過期)
*/
private boolean isCredentialsNonExpired = true;
/**
* 帳戶是否可用(1 可用,0 刪除用戶)
*/
private boolean isEnabled = true;
/**
* 權(quán)限列表
*/
@TableField(exist = false)
Collection<? extends GrantedAuthority> authorities;3.3.編寫Service接口
public interface UserService extends IService<User> {
/**
* 根據(jù)用戶名查詢用戶信息
* @param userName
* @return
*/
User findUserByUserName(String userName);
}3.4.編寫ServiceImpl
package com.manong.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.manong.entity.User;
import com.manong.dao.UserMapper;
import com.manong.service.UserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
* <p>
* 服務(wù)實(shí)現(xiàn)類
* </p>
*
* @author lemon
* @since 2022-12-06
*/
@Service
@Transactional
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
@Override
public User findUserByUserName(String username) {
//創(chuàng)建條件構(gòu)造器對(duì)象
QueryWrapper queryWrapper=new QueryWrapper();
queryWrapper.eq("username",username);
//執(zhí)行查詢
return baseMapper.selectOne(queryWrapper);
}
}3.5. 自定義UserDetailsService類
package com.manong.config.security.service;
import com.manong.entity.Permission;
import com.manong.entity.User;
import com.manong.service.PermissionService;
import com.manong.service.UserService;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
/*
* 用戶認(rèn)證處理器類
* */
@Component
public class CustomerUserDetailService implements UserDetailsService {
@Resource
private UserService userService;
@Resource
private PermissionService permissionService;
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException{
//根據(jù)對(duì)象查找用戶信息
User user = userService.findUserByUserName(username);
//判斷對(duì)象是否為空
if(user==null){
throw new UsernameNotFoundException("用戶的賬號(hào)密碼錯(cuò)誤");
}
//查詢當(dāng)前登錄用戶擁有權(quán)限列表
List<Permission> permissionList = permissionService.findPermissionListByUserId(user.getId());
//獲取對(duì)應(yīng)的權(quán)限編碼
List<String> codeList = permissionList.stream()
.filter(Objects::nonNull)
.map(item -> item.getCode())
.filter(Objects::nonNull)
.collect(Collectors.toList());
//將權(quán)限編碼轉(zhuǎn)換成數(shù)據(jù)
String [] strings=codeList.toArray(new String[codeList.size()]);
//設(shè)置權(quán)限列表
List<GrantedAuthority> authorityList = AuthorityUtils.createAuthorityList(strings);
//將權(quán)限列表設(shè)置給User
user.setAuthorities(authorityList);
//設(shè)置該用戶擁有的菜單
user.setPermissionList(permissionList);
//查詢成功
return user;
}
}四.通常情況下,我們需要自定義四個(gè)類來獲取處理類 包括成功,失敗,匿名用戶,登錄了但沒有權(quán)限的用戶
4.1.成功
package com.manong.config.security.handler;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.manong.entity.User;
import com.manong.utils.JwtUtils;
import com.manong.utils.LoginResult;
import com.manong.utils.ResultCode;
import io.jsonwebtoken.Jwts;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
import sun.net.www.protocol.http.AuthenticationHeader;
import javax.annotation.Resource;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import com.alibaba.fastjson.JSON;
/*
* 登錄認(rèn)證成功處理器類
* */
@Component
public class LoginSuccessHandler implements AuthenticationSuccessHandler {
@Resource
private JwtUtils jwtUtils;
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
//設(shè)置相應(yīng)編碼格式
response.setContentType("application/json;charset-utf-8");
//獲取當(dāng)前登錄用戶的信息
User user = (User) authentication.getPrincipal();
//創(chuàng)建token對(duì)象
String token = jwtUtils.generateToken(user);
//設(shè)置token的秘鑰和過期時(shí)間
long expireTime = Jwts.parser()
.setSigningKey(jwtUtils.getSecret())
.parseClaimsJws(token.replace("jwt_", ""))
.getBody().getExpiration().getTime();//設(shè)置過期時(shí)間
//創(chuàng)建LOgin登錄對(duì)象
LoginResult loginResult=new LoginResult(user.getId(), ResultCode.SUCCESS,token,expireTime);
//將對(duì)象轉(zhuǎn)換成json格式
//消除循環(huán)引用
String result = JSON.toJSONString(loginResult, SerializerFeature.DisableCircularReferenceDetect);
//獲取輸出流
ServletOutputStream outputStream = response.getOutputStream();
outputStream.write(result.getBytes(StandardCharsets.UTF_8));
outputStream.flush();
outputStream.close();
}
}4.2 失敗
package com.manong.config.security.handler;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.baomidou.mybatisplus.extension.api.R;
import com.manong.entity.User;
import com.manong.utils.Result;
import com.manong.utils.ResultCode;
import org.springframework.security.authentication.*;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
@Component
public class LoginFailureHandler implements AuthenticationFailureHandler {
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
//設(shè)置相應(yīng)編碼格式
response.setContentType("application/json;charset-utf-8");
//獲取輸出流
ServletOutputStream outputStream = response.getOutputStream();
//定義變量,保存異常信息
String message=null;
//判斷異常類型
if(exception instanceof AccountExpiredException){
message="賬戶過期失敗";
}
else if(exception instanceof BadCredentialsException){
message="用戶名的賬號(hào)密碼錯(cuò)誤,登錄失敗";
}
else if(exception instanceof CredentialsExpiredException){
message="密碼過期,登錄失敗";
}
else if(exception instanceof DisabledException){
message="賬號(hào)過期,登錄失敗";
}
else if(exception instanceof LockedException){
message="賬號(hào)被上鎖,登錄失敗";
}
else if(exception instanceof InternalAuthenticationServiceException){
message="用戶不存在";
}
else {
message="登錄失敗";
}
//將結(jié)果轉(zhuǎn)換為Json格式
String result = JSON.toJSONString(Result.error().code(ResultCode.ERROR).message(message));
//將結(jié)果保存到輸出中
outputStream.write(result.getBytes(StandardCharsets.UTF_8));
outputStream.flush();
outputStream.close();
}
}4.3 匿名無用戶
package com.manong.config.security.handler;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.manong.entity.User;
import com.manong.utils.Result;
import com.manong.utils.ResultCode;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
import sun.net.www.protocol.http.AuthenticationHeader;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import com.alibaba.fastjson.JSON;
/*
* 匿名訪問無權(quán)限資源處理器
* */
@Component
public class AnonymousAuthenticationHandler implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
response.setContentType("application/json;charset-utf-8");
//獲取輸出流
ServletOutputStream outputStream = response.getOutputStream();
//將對(duì)象轉(zhuǎn)換成json格式
//消除循環(huán)引用
String result = JSON.toJSONString(Result.error().code(ResultCode.NO_AUTH).message("匿名用戶無權(quán)限訪問"), SerializerFeature.DisableCircularReferenceDetect);
//獲取輸出流
outputStream.write(result.getBytes(StandardCharsets.UTF_8));
outputStream.flush();
outputStream.close();
}
}4.4 登錄了但是沒有權(quán)限的用戶
package com.manong.config.security.handler;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.manong.entity.User;
import com.manong.utils.Result;
import com.manong.utils.ResultCode;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
import sun.net.www.protocol.http.AuthenticationHeader;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import com.alibaba.fastjson.JSON;
/*
* 認(rèn)證用戶訪問無權(quán)限資源處理器
* */
@Component
public class CustomerAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
response.setContentType("application/json;charset-utf-8");
ServletOutputStream outputStream = response.getOutputStream();
//將對(duì)象轉(zhuǎn)換成json格式
//消除循環(huán)引用
String result = JSON.toJSONString(Result.error().code(ResultCode.NO_AUTH).message("用戶無權(quán)限訪問,請(qǐng)聯(lián)系教務(wù)處"), SerializerFeature.DisableCircularReferenceDetect);
//獲取輸出流
outputStream.write(result.getBytes(StandardCharsets.UTF_8));
outputStream.flush();
outputStream.close();
}
}五.編寫SpringSecurity配置類,把上面的四個(gè)類進(jìn)行合并
package com.manong.config.security.service;
import com.manong.config.security.handler.AnonymousAuthenticationHandler;
import com.manong.config.security.handler.CustomerAccessDeniedHandler;
import com.manong.config.security.handler.LoginFailureHandler;
import com.manong.config.security.handler.LoginSuccessHandler;
import org.springframework.context.annotation.Bean;
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.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
@Component
@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
@Resource
private LoginSuccessHandler loginSuccessHandler;
@Resource
private LoginFailureHandler loginFailureHandler;
@Resource
private CustomerAccessDeniedHandler customerAccessDeniedHandler;
@Resource
private AnonymousAuthenticationHandler anonymousAuthenticationHandler;
@Resource
private CustomerUserDetailService customerUserDetailService;
//注入加密類
@Bean
public BCryptPasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
//處理登錄認(rèn)證
@Override
protected void configure(HttpSecurity http) throws Exception {
//登錄過程處理
http.formLogin() //表單登錄
.loginProcessingUrl("/api/user/login") //登錄請(qǐng)求url地址
.successHandler(loginSuccessHandler) //認(rèn)證成功
.failureHandler(loginFailureHandler) //認(rèn)證失敗
.and()
.csrf().disable()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS) //不創(chuàng)建Session
.and().authorizeRequests() //設(shè)置需要攔截的請(qǐng)求
.antMatchers("/api/user/login").permitAll()//登錄放行
.anyRequest().authenticated() //其他請(qǐng)求一律攔截
.and()
.exceptionHandling()
.authenticationEntryPoint(anonymousAuthenticationHandler) //匿名無權(quán)限類
.accessDeniedHandler(customerAccessDeniedHandler) //認(rèn)證用戶無權(quán)限
.and()
.cors();//支持跨域
}
//認(rèn)證配置處理器
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(customerUserDetailService)
.passwordEncoder(this.passwordEncoder());//密碼加密
}
}到此這篇關(guān)于Springboot整合SpringSecurity的文章就介紹到這了,更多相關(guān)Springboot整合SpringSecurity內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- SpringBoot啟動(dòng)security后如何關(guān)閉彈出的/login頁面
- SpringBoot整合Spring Security構(gòu)建安全的Web應(yīng)用
- SpringBoot整合新版SpringSecurity完整過程
- SpringBoot集成Swagger使用SpringSecurity控制訪問權(quán)限問題
- SpringBoot集成SpringSecurity安全框架方式
- SpringSecurity在SpringBoot中的自動(dòng)裝配過程
- Springbootadmin與security沖突問題及解決
- SpringBoot整合Springsecurity實(shí)現(xiàn)數(shù)據(jù)庫登錄及權(quán)限控制功能
- SpringBoot配置Spring?Security的實(shí)現(xiàn)示例
相關(guān)文章
springboot靜態(tài)資源(static)無法訪問問題404及解決過程
文章瀏覽閱讀7.8k次。本文介紹了SpringBoot項(xiàng)目中遇到的靜態(tài)資源無法訪問的問題及解決辦法。主要從攔截器配置、靜態(tài)資源映射配置和pom.xml文件配置三個(gè)方面進(jìn)行詳細(xì)說明。2026-05-05
maven依賴關(guān)系中的<scope>provided</scope>使用詳解
這篇文章主要介紹了maven依賴關(guān)系中的<scope>provided</scope>使用詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07
Java中InputSteam怎么轉(zhuǎn)String
面了一位實(shí)習(xí)生,叫他給我說一下怎么把InputStream轉(zhuǎn)換為String,這種常規(guī)的操作,他竟然都沒有用過我準(zhǔn)備結(jié)合工作經(jīng)驗(yàn),整理匯集出了InputStream 到String 轉(zhuǎn)換的十八般武藝,助大家闖蕩 Java 江湖一臂之力,需要的朋友可以參考下2021-06-06
2020年IntelliJ IDEA最新最詳細(xì)配置圖文教程詳解
這篇文章主要介紹了2020年IntelliJ IDEA最新最詳細(xì)配置圖文教程詳解,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-02-02
解決Spring boot 嵌入的tomcat不啟動(dòng)問題
這篇文章主要介紹了解決Spring boot 嵌入的tomcat不啟動(dòng)問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-10-10
Java多種方式實(shí)現(xiàn)Excel轉(zhuǎn)Pdf的保姆級(jí)教程
在企業(yè)級(jí)系統(tǒng)使用或日常使用中,我們經(jīng)常使用excel表格數(shù)據(jù)進(jìn)行瀏覽數(shù)據(jù),但有時(shí)候我們會(huì)想要將excel轉(zhuǎn)換成pdf進(jìn)行預(yù)覽使用,本篇博文以java進(jìn)行編寫多種實(shí)現(xiàn)方式實(shí)現(xiàn)多sheet頁excel轉(zhuǎn)換一個(gè)pdf的需求,需要的朋友可以參考下2025-08-08
jackson json序列化實(shí)現(xiàn)首字母大寫,第二個(gè)字母需小寫
這篇文章主要介紹了jackson json序列化實(shí)現(xiàn)首字母大寫,第二個(gè)字母需小寫方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-06-06
IDEA教程創(chuàng)建SpringBoot前后端分離項(xiàng)目示例圖解
在使用spring、mybatis等框架時(shí),配置文件很復(fù)雜,有時(shí)復(fù)雜的讓人想放棄Java,使用C#。springboot出現(xiàn)這一切問題就都不是問題2021-10-10

