springSecurity+jwt使用小結(jié)
(1) jdk版本和springboot版本
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<springboot.version>3.1.5</springboot.version>
</properties>(2) 流程說明(可以對照代碼實現(xiàn))
1.springboot啟動時,會先加載WebSecurityConfig配置
(1)WebSecurityConfig里會跳過指定的url【requestMatchers("/auth/login").permitAll()】
(2)增加過濾器【.addFilterBefore(jwtAuthenticationTokenFilter(), UsernamePasswordAuthenticationFilter.class)】
(3)綁定認證失敗類:exceptionHandling(exce->exce.authenticationEntryPoint(unauthorizedHandler))
(4)綁定權(quán)限校驗失敗類:exceptionHandling(exce->exce.accessDeniedHandler(wAccessDeniedHandler))
2.當/auth/login請求進入時,會先到JwtAuthenticationTokenFilter過濾器,判斷請求頭中是否有token,因為沒有token直接filterChain.doFilter(request, response)下一步,又因為在WebSecurityConfig配置了過濾,不會進入異常類,會直接到達AuthController,會進入到LoginServiceImpl類,根據(jù)用戶名和密碼進行校驗【authenticationManager.authenticate(authentication),真正進行校驗的實現(xiàn)類JwtAuthenticationProvider】,校驗通過則返回token,否則拋出異常,返回錯誤信息。
3.當其它請求進入時,也會先到JwtAuthenticationTokenFilter過濾器,如果有token,則解析token,獲取用戶信息,然后設(shè)置到SecurityContextHolder中,如果解析失敗,則拋出異常,進入異常處理類,返回錯誤信息。如果沒有token,則會被攔截,進入異常處理類,返回錯誤信息
(3) 代碼實現(xiàn)
1.spring Security配置WebSecurityConfig
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.authentication.configuration.EnableGlobalAuthentication;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
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.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@Configuration
@EnableWebSecurity
@EnableMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig {
@Autowired
private UnauthorizedHandler unauthorizedHandler;
@Autowired
private WAccessDeniedHandler wAccessDeniedHandler;
/**
* 認證管理
* @param configuration
* @return
* @throws Exception
*/
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration configuration) throws Exception {
return configuration.getAuthenticationManager();
}
/**
* 認證過濾器
* @return
*/
@Bean
public JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter(){
return new JwtAuthenticationTokenFilter();
}
/**
* 密碼加密
* @return
*/
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
/**
* 權(quán)限校驗
* @return
*/
@Bean("per")
public PermissionCheckServiceImpl permissionCheckServiceImpl(){
return new PermissionCheckServiceImpl();
}
/**
* 配置安全過濾器鏈
* @param httpSecurity
* @return
* @throws Exception
*/
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
return httpSecurity
.csrf(AbstractHttpConfigurer::disable)
.sessionManagement(sessionManager-> sessionManager.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(authorize->authorize
.requestMatchers("/auth/login").permitAll()
.anyRequest().authenticated())
.formLogin(Customizer.withDefaults())
.httpBasic(Customizer.withDefaults())
//禁用緩存
.headers(header->header.cacheControl(HeadersConfigurer.CacheControlConfig::disable))
.addFilterBefore(jwtAuthenticationTokenFilter(), UsernamePasswordAuthenticationFilter.class)
//綁定認證失敗類
// .exceptionHandling(exce->exce.authenticationEntryPoint(unauthorizedHandler))
//鑒權(quán)失敗類
.exceptionHandling(exce->exce.accessDeniedHandler(wAccessDeniedHandler))
.build();
}
}2.jwt身份攔截器JwtAuthenticationTokenFilter
import cn.hutool.core.convert.NumberWithFormat;
import cn.hutool.json.JSONUtil;
import cn.hutool.jwt.JWT;
import cn.hutool.jwt.JWTUtil;
import cn.hutool.jwt.JWTValidator;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.web.filter.OncePerRequestFilter;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
/**
*
* 身份驗證攔截器
*/
@Slf4j
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
@Autowired
private UserDetailsService userDetailsService;
@Override
protected void doFilterInternal(HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull FilterChain filterChain) throws ServletException, IOException, UsernameNotFoundException {
//從頭中獲取token(jwt)
String authorization = request.getHeader("Authorization");
//判斷token
if(StringUtils.isBlank(authorization)){
filterChain.doFilter(request, response);
return ;
}
//校驗token格式
if(!authorization.startsWith("Bearer ")){
log.error(RespCodeEnum.TOKEN_ERROR.getDesc());
response(response, RespCodeEnum.TOKEN_ERROR);
return ;
}
//獲取jwt數(shù)據(jù)
String token = authorization.split(" ")[1];
if(!JWTUtil.verify(token, AuthConstant.JWT_KEY.getBytes(StandardCharsets.UTF_8))){
log.error(RespCodeEnum.TOKEN_ERROR.getDesc());
response(response, RespCodeEnum.TOKEN_ERROR);
}
//獲取用戶名和過期時間
JWT jwt = JWTUtil.parseToken(token);
String loginname = (String) jwt.getPayload("loginname");
//獲取jwt中的過期時間
long exp = ((NumberWithFormat) jwt.getPayload("exp")).longValue();
//判斷是否已經(jīng)過期
if(System.currentTimeMillis() / 1000 > exp){
log.error(RespCodeEnum.TOKEN_EXP.getDesc());
response(response, RespCodeEnum.TOKEN_EXP);
return;
}
//獲取用戶信息
UserDetailsBo userDetails = (UserDetailsBo)userDetailsService.loadUserByUsername(loginname);
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userDetails.getUsername(),
userDetails.getPassword(), userDetails.getAuthorities());
authenticationToken.setDetails(userDetails.getUserDto());
//將認證過了憑證保存到security的上下文中以便于在程序中使用
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
filterChain.doFilter(request,response);
}
private void response(@NotNull HttpServletResponse response,@NotNull RespCodeEnum error) throws IOException {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); // 或者使用自定義狀態(tài)碼
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
response.setCharacterEncoding("UTF-8");
response.getWriter().write(JSONUtil.toJsonStr(ResponseDto.fail(error)));
}
}
3.自定義身份驗證失敗處理器類UnauthorizedHandler
import cn.hutool.json.JSONUtil;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;
import java.io.IOException;
/**
*
* 自定義身份驗證失敗處理器
*/
@Component
public class UnauthorizedHandler implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException {
// 設(shè)置響應(yīng)狀態(tài)碼為401(未授權(quán))
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
// 設(shè)置響應(yīng)內(nèi)容類型
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
// 響應(yīng)體內(nèi)容,可以根據(jù)需要自定義
ResponseDto fail = ResponseDto.fail(RespCodeEnum.ACCESS_DENIED);
response.getWriter().write(JSONUtil.toJsonStr(fail));
}
}
4.權(quán)限認證失敗處理類WAccessDeniedHandler
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;
import java.io.IOException;
/**
*
* 權(quán)限認證失敗處理
*/
@Component
public class WAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json");
response.getWriter().print("認證失敗");
response.getWriter().flush();
}
}
5.JwtAuthenticationProvider實現(xiàn)AuthenticationProvider接口,進行用戶身份驗證
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
/**
* 用戶身份驗證
*
*/
@Component
public class JwtAuthenticationProvider implements AuthenticationProvider {
@Autowired
private PasswordEncoder passwordEncoder;
@Autowired
private UserDetailsService userDetailsService;
@Override
public Authentication authenticate(Authentication authentication) {
String username = String.valueOf(authentication.getPrincipal());
String password = String.valueOf(authentication.getCredentials());
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
if(userDetails != null && StringUtils.isNotBlank(userDetails.getPassword())
&& userDetails.getPassword().equals(password)){
return new UsernamePasswordAuthenticationToken(username,password,authentication.getAuthorities());
}
throw new BusinessException(RespCodeEnum.NAME_OR_PASSWORD_ERROR);
}
@Override
public boolean supports(Class<?> authentication) {
return UsernamePasswordAuthenticationToken.class.equals(authentication);
}
}
6.繼承UserDetailsService,從數(shù)據(jù)庫獲取用戶信息
import lombok.RequiredArgsConstructor;
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.Service;
@Service
@RequiredArgsConstructor
public class UserDetailsServiceImpl implements UserDetailsService {
private final IUserService userService;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
UserDto user = userService.selectUserByLoginName(username);
return new UserDetailsBo(user);
}
}
7.自定義UserDetailsBo類,繼承UserDetails
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import java.util.Collection;
import java.util.stream.Collectors;
@Component
public class UserDetailsBo implements UserDetails {
private UserDto userDto;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return userDto.getPermissionName().stream()
.map(SimpleGrantedAuthority::new).collect(Collectors.toList());
}
@Override
public String getPassword() {
return userDto.getPassword();
}
@Override
public String getUsername() {
return userDto.getLoginName();
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
public UserDetailsBo(){}
public UserDetailsBo(UserDto userDto){
this.userDto = userDto;
}
public UserDto getUserDto() {
return userDto;
}
public void setUserDto(UserDto userDto) {
this.userDto = userDto;
}
}
8.自定義權(quán)限校驗PermissionCheckServiceImpl
import cn.hutool.core.util.ArrayUtil;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.util.CollectionUtils;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
public class PermissionCheckServiceImpl {
public PermissionCheckServiceImpl(){}
public boolean havePermission(String... permissions)
{
if(permissions == null){
return true;
}
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if(authentication != null){
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
List<String> authList = authorities.stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList());
for(int i = 0;i < permissions.length;i++){
if(authList.contains(permissions[i])){
return true;
}
}
}
return false;
}
}
9.實現(xiàn)登錄接口和接口權(quán)限校驗
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 認證控制器
*
*/
@RestController
@RequestMapping("auth")
@RequiredArgsConstructor
public class AuthController {
private final ILoginService loginService;
/**
* 登錄
* @param req 請求參數(shù)
* @return 返回token
*/
@GetMapping("login")
public String login(@Validated UserLoginAccPwdDto req) {
return loginService.loginAccPwd(req);
}
@PreAuthorize("@per.havePermission('user','admin')")
@GetMapping("test")
public UserInfoVo test() {
return null;
}
}
10.登錄實現(xiàn)
import cn.hutool.jwt.JWT;
import lombok.RequiredArgsConstructor;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.stereotype.Service;
import java.nio.charset.StandardCharsets;
import java.util.Date;
@Service
@RequiredArgsConstructor
public class LoginServiceImpl implements ILoginService {
private final AuthenticationManager authenticationManager;
@Override
public String loginAccPwd(UserLoginAccPwdDto login) {
//登錄驗證
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(login.getLoginName(), login.getPassword());
authenticationManager.authenticate(authentication);
//生成jwt token
String token = JWT.create()
.setPayload("loginname", login.getLoginName())
.setKey(AuthConstant.JWT_KEY.getBytes(StandardCharsets.UTF_8))
//過期時間3小時
.setExpiresAt(new Date(System.currentTimeMillis() + 1000 * 60 * 60))
.sign();
return token;
}
}到此這篇關(guān)于springSecurity+jwt使用小結(jié)的文章就介紹到這了,更多相關(guān)springSecurity使用jwt內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希
- SpringSecurity+Redis+Jwt實現(xiàn)用戶認證授權(quán)
- springboot+springsecurity+mybatis+JWT+Redis?實現(xiàn)前后端離實戰(zhàn)教程
- SpringBoot3.0+SpringSecurity6.0+JWT的實現(xiàn)
- SpringSecurity整合JWT的使用示例
- SpringBoot整合SpringSecurity和JWT和Redis實現(xiàn)統(tǒng)一鑒權(quán)認證
- SpringBoot+SpringSecurity+jwt實現(xiàn)驗證
- SpringSecurity詳解整合JWT實現(xiàn)全過程
- mall整合SpringSecurity及JWT認證授權(quán)實戰(zhàn)下
- mall整合SpringSecurity及JWT實現(xiàn)認證授權(quán)實戰(zhàn)
- Java SpringSecurity+JWT實現(xiàn)登錄認證
相關(guān)文章
源碼解讀Spring-Integration執(zhí)行過程
Spring-Integration基于Spring,在應(yīng)用程序中啟用了輕量級消息傳遞,并支持通過聲明式適配器與外部系統(tǒng)集成,今天主要是看個簡單的hello word進來分析下整個執(zhí)行過程,感興趣的朋友一起看看吧2021-06-06
intellij idea14打包apk文件和查看sha1值
這篇文章主要為大家詳細介紹了intellij idea14打包apk文件和查看sha1值,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2020-10-10
MyBatis基礎(chǔ)支持DataSource實現(xiàn)源碼解析
這篇文章主要為大家介紹了MyBatis基礎(chǔ)支持DataSource實現(xiàn)源碼解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-02-02
Java技能點之SimpleDateFormat進行日期格式化問題
這篇文章主要介紹了Java技能點之SimpleDateFormat進行日期格式化問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-04-04
使用java生成json時產(chǎn)生棧溢出錯誤問題及解決方案
這篇文章主要介紹了使用java生成json時產(chǎn)生棧溢出錯誤問題及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-06-06
SpringBoot單元測試使用@Test沒有run方法的解決方案
這篇文章主要介紹了SpringBoot單元測試使用@Test沒有run方法的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-01-01
java運行時數(shù)據(jù)區(qū)域和類結(jié)構(gòu)詳解
這篇文章主要介紹了java運行時數(shù)據(jù)區(qū)域和類結(jié)構(gòu),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-07-07

