Springboot整合SpringSecurity的完整案例詳解
一.Spring Security介紹
Spring Security是基于Spring生態(tài)圈的,用于提供安全訪問控制解決方案的框架。Spring Security的安 全管理有兩個重要概念,分別是Authentication(認證)和Authorization(授權)。 為了方便Spring Boot項目的安全管理,Spring Boot對Spring Security安全框架進行了整合支持,并提 供了通用的自動化配置,從而實現(xiàn)了Spring Security安全框架中包含的多數(shù)安全管理功能。
Spring Security登錄認證主要涉及兩個重要的接口 UserDetailService和UserDetails接口。
UserDetailService接口主要定義了一個方法 loadUserByUsername(String username)用于完成用戶信息的查 詢,其中username就是登錄時的登錄名稱,登錄認證時,需要自定義一個實現(xiàn)類實現(xiàn)UserDetailService接 口,完成數(shù)據(jù)庫查詢,該接口返回UserDetail。
UserDetail主要用于封裝認證成功時的用戶信息,即UserDetailService返回的用戶信息,可以用Spring
自己的User對象,但是最好是實現(xiàn)UserDetail接口,自定義用戶對象。
二.Spring Security認證步驟
1. 自定UserDetails類:當實體對象字段不滿足時需要自定義UserDetails,一般都要自定義
UserDetails。
2. 自定義UserDetailsService類,主要用于從數(shù)據(jù)庫查詢用戶信息。
3. 創(chuàng)建登錄認證成功處理器,認證成功后需要返回JSON數(shù)據(jù),菜單權限等。
4. 創(chuàng)建登錄認證失敗處理器,認證失敗需要返回JSON數(shù)據(jù),給前端判斷。
5. 創(chuàng)建匿名用戶訪問無權限資源時處理器,匿名用戶訪問時,需要提示JSON。
6. 創(chuàng)建認證過的用戶訪問無權限資源時的處理器,無權限訪問時,需要提示JSON。
7. 配置Spring Security配置類,把上面自定義的處理器交給Spring Security。
三.Spring Security認證實現(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
當實體對象字段不滿足時Spring Security認證時,需要自定義UserDetails。
1. 將User類實現(xiàn)UserDetails接口
2. 將原有的isAccountNonExpired、isAccountNonLocked、isCredentialsNonExpired和isEnabled屬性修 改成boolean類型,同時添加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;
/**
* 權限列表
*/
@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>
* 服務實現(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)建條件構造器對象
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;
/*
* 用戶認證處理器類
* */
@Component
public class CustomerUserDetailService implements UserDetailsService {
@Resource
private UserService userService;
@Resource
private PermissionService permissionService;
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException{
//根據(jù)對象查找用戶信息
User user = userService.findUserByUserName(username);
//判斷對象是否為空
if(user==null){
throw new UsernameNotFoundException("用戶的賬號密碼錯誤");
}
//查詢當前登錄用戶擁有權限列表
List<Permission> permissionList = permissionService.findPermissionListByUserId(user.getId());
//獲取對應的權限編碼
List<String> codeList = permissionList.stream()
.filter(Objects::nonNull)
.map(item -> item.getCode())
.filter(Objects::nonNull)
.collect(Collectors.toList());
//將權限編碼轉換成數(shù)據(jù)
String [] strings=codeList.toArray(new String[codeList.size()]);
//設置權限列表
List<GrantedAuthority> authorityList = AuthorityUtils.createAuthorityList(strings);
//將權限列表設置給User
user.setAuthorities(authorityList);
//設置該用戶擁有的菜單
user.setPermissionList(permissionList);
//查詢成功
return user;
}
}四.通常情況下,我們需要自定義四個類來獲取處理類 包括成功,失敗,匿名用戶,登錄了但沒有權限的用戶
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;
/*
* 登錄認證成功處理器類
* */
@Component
public class LoginSuccessHandler implements AuthenticationSuccessHandler {
@Resource
private JwtUtils jwtUtils;
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
//設置相應編碼格式
response.setContentType("application/json;charset-utf-8");
//獲取當前登錄用戶的信息
User user = (User) authentication.getPrincipal();
//創(chuàng)建token對象
String token = jwtUtils.generateToken(user);
//設置token的秘鑰和過期時間
long expireTime = Jwts.parser()
.setSigningKey(jwtUtils.getSecret())
.parseClaimsJws(token.replace("jwt_", ""))
.getBody().getExpiration().getTime();//設置過期時間
//創(chuàng)建LOgin登錄對象
LoginResult loginResult=new LoginResult(user.getId(), ResultCode.SUCCESS,token,expireTime);
//將對象轉換成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 {
//設置相應編碼格式
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="用戶名的賬號密碼錯誤,登錄失敗";
}
else if(exception instanceof CredentialsExpiredException){
message="密碼過期,登錄失敗";
}
else if(exception instanceof DisabledException){
message="賬號過期,登錄失敗";
}
else if(exception instanceof LockedException){
message="賬號被上鎖,登錄失敗";
}
else if(exception instanceof InternalAuthenticationServiceException){
message="用戶不存在";
}
else {
message="登錄失敗";
}
//將結果轉換為Json格式
String result = JSON.toJSONString(Result.error().code(ResultCode.ERROR).message(message));
//將結果保存到輸出中
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;
/*
* 匿名訪問無權限資源處理器
* */
@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();
//將對象轉換成json格式
//消除循環(huán)引用
String result = JSON.toJSONString(Result.error().code(ResultCode.NO_AUTH).message("匿名用戶無權限訪問"), SerializerFeature.DisableCircularReferenceDetect);
//獲取輸出流
outputStream.write(result.getBytes(StandardCharsets.UTF_8));
outputStream.flush();
outputStream.close();
}
}4.4 登錄了但是沒有權限的用戶
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;
/*
* 認證用戶訪問無權限資源處理器
* */
@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();
//將對象轉換成json格式
//消除循環(huán)引用
String result = JSON.toJSONString(Result.error().code(ResultCode.NO_AUTH).message("用戶無權限訪問,請聯(lián)系教務處"), SerializerFeature.DisableCircularReferenceDetect);
//獲取輸出流
outputStream.write(result.getBytes(StandardCharsets.UTF_8));
outputStream.flush();
outputStream.close();
}
}五.編寫SpringSecurity配置類,把上面的四個類進行合并
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();
}
//處理登錄認證
@Override
protected void configure(HttpSecurity http) throws Exception {
//登錄過程處理
http.formLogin() //表單登錄
.loginProcessingUrl("/api/user/login") //登錄請求url地址
.successHandler(loginSuccessHandler) //認證成功
.failureHandler(loginFailureHandler) //認證失敗
.and()
.csrf().disable()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS) //不創(chuàng)建Session
.and().authorizeRequests() //設置需要攔截的請求
.antMatchers("/api/user/login").permitAll()//登錄放行
.anyRequest().authenticated() //其他請求一律攔截
.and()
.exceptionHandling()
.authenticationEntryPoint(anonymousAuthenticationHandler) //匿名無權限類
.accessDeniedHandler(customerAccessDeniedHandler) //認證用戶無權限
.and()
.cors();//支持跨域
}
//認證配置處理器
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(customerUserDetailService)
.passwordEncoder(this.passwordEncoder());//密碼加密
}
}到此這篇關于Springboot整合SpringSecurity的文章就介紹到這了,更多相關Springboot整合SpringSecurity內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
- SpringBoot啟動security后如何關閉彈出的/login頁面
- SpringBoot整合Spring Security構建安全的Web應用
- SpringBoot整合新版SpringSecurity完整過程
- SpringBoot集成Swagger使用SpringSecurity控制訪問權限問題
- SpringBoot集成SpringSecurity安全框架方式
- SpringSecurity在SpringBoot中的自動裝配過程
- Springbootadmin與security沖突問題及解決
- SpringBoot整合Springsecurity實現(xiàn)數(shù)據(jù)庫登錄及權限控制功能
- SpringBoot配置Spring?Security的實現(xiàn)示例
相關文章
springboot靜態(tài)資源(static)無法訪問問題404及解決過程
文章瀏覽閱讀7.8k次。本文介紹了SpringBoot項目中遇到的靜態(tài)資源無法訪問的問題及解決辦法。主要從攔截器配置、靜態(tài)資源映射配置和pom.xml文件配置三個方面進行詳細說明。2026-05-05
maven依賴關系中的<scope>provided</scope>使用詳解
這篇文章主要介紹了maven依賴關系中的<scope>provided</scope>使用詳解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-07-07
2020年IntelliJ IDEA最新最詳細配置圖文教程詳解
這篇文章主要介紹了2020年IntelliJ IDEA最新最詳細配置圖文教程詳解,本文通過圖文并茂的形式給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-02-02
Java多種方式實現(xiàn)Excel轉Pdf的保姆級教程
在企業(yè)級系統(tǒng)使用或日常使用中,我們經(jīng)常使用excel表格數(shù)據(jù)進行瀏覽數(shù)據(jù),但有時候我們會想要將excel轉換成pdf進行預覽使用,本篇博文以java進行編寫多種實現(xiàn)方式實現(xiàn)多sheet頁excel轉換一個pdf的需求,需要的朋友可以參考下2025-08-08
jackson json序列化實現(xiàn)首字母大寫,第二個字母需小寫
這篇文章主要介紹了jackson json序列化實現(xiàn)首字母大寫,第二個字母需小寫方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-06-06
IDEA教程創(chuàng)建SpringBoot前后端分離項目示例圖解
在使用spring、mybatis等框架時,配置文件很復雜,有時復雜的讓人想放棄Java,使用C#。springboot出現(xiàn)這一切問題就都不是問題2021-10-10

