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) 流程說(shuō)明(可以對(duì)照代碼實(shí)現(xiàn))
1.springboot啟動(dòng)時(shí),會(huì)先加載WebSecurityConfig配置
(1)WebSecurityConfig里會(huì)跳過(guò)指定的url【requestMatchers("/auth/login").permitAll()】
(2)增加過(guò)濾器【.addFilterBefore(jwtAuthenticationTokenFilter(), UsernamePasswordAuthenticationFilter.class)】
(3)綁定認(rèn)證失敗類:exceptionHandling(exce->exce.authenticationEntryPoint(unauthorizedHandler))
(4)綁定權(quán)限校驗(yàn)失敗類:exceptionHandling(exce->exce.accessDeniedHandler(wAccessDeniedHandler))
2.當(dāng)/auth/login請(qǐng)求進(jìn)入時(shí),會(huì)先到JwtAuthenticationTokenFilter過(guò)濾器,判斷請(qǐng)求頭中是否有token,因?yàn)闆]有token直接filterChain.doFilter(request, response)下一步,又因?yàn)樵赪ebSecurityConfig配置了過(guò)濾,不會(huì)進(jìn)入異常類,會(huì)直接到達(dá)AuthController,會(huì)進(jìn)入到LoginServiceImpl類,根據(jù)用戶名和密碼進(jìn)行校驗(yàn)【authenticationManager.authenticate(authentication),真正進(jìn)行校驗(yàn)的實(shí)現(xiàn)類JwtAuthenticationProvider】,校驗(yàn)通過(guò)則返回token,否則拋出異常,返回錯(cuò)誤信息。
3.當(dāng)其它請(qǐng)求進(jìn)入時(shí),也會(huì)先到JwtAuthenticationTokenFilter過(guò)濾器,如果有token,則解析token,獲取用戶信息,然后設(shè)置到SecurityContextHolder中,如果解析失敗,則拋出異常,進(jìn)入異常處理類,返回錯(cuò)誤信息。如果沒有token,則會(huì)被攔截,進(jìn)入異常處理類,返回錯(cuò)誤信息
(3) 代碼實(shí)現(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;
/**
* 認(rèn)證管理
* @param configuration
* @return
* @throws Exception
*/
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration configuration) throws Exception {
return configuration.getAuthenticationManager();
}
/**
* 認(rèn)證過(guò)濾器
* @return
*/
@Bean
public JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter(){
return new JwtAuthenticationTokenFilter();
}
/**
* 密碼加密
* @return
*/
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
/**
* 權(quán)限校驗(yàn)
* @return
*/
@Bean("per")
public PermissionCheckServiceImpl permissionCheckServiceImpl(){
return new PermissionCheckServiceImpl();
}
/**
* 配置安全過(guò)濾器鏈
* @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)
//綁定認(rèn)證失敗類
// .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;
/**
*
* 身份驗(yàn)證攔截器
*/
@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 ;
}
//校驗(yàn)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);
}
//獲取用戶名和過(guò)期時(shí)間
JWT jwt = JWTUtil.parseToken(token);
String loginname = (String) jwt.getPayload("loginname");
//獲取jwt中的過(guò)期時(shí)間
long exp = ((NumberWithFormat) jwt.getPayload("exp")).longValue();
//判斷是否已經(jīng)過(guò)期
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());
//將認(rèn)證過(guò)了憑證保存到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.自定義身份驗(yàn)證失敗處理器類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;
/**
*
* 自定義身份驗(yàn)證失敗處理器
*/
@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)限認(rè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)限認(rè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("認(rèn)證失敗");
response.getWriter().flush();
}
}
5.JwtAuthenticationProvider實(shí)現(xiàn)AuthenticationProvider接口,進(jìn)行用戶身份驗(yàn)證
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;
/**
* 用戶身份驗(yàn)證
*
*/
@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ù)庫(kù)獲取用戶信息
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)限校驗(yà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.實(shí)現(xiàn)登錄接口和接口權(quán)限校驗(yà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;
/**
* 認(rèn)證控制器
*
*/
@RestController
@RequestMapping("auth")
@RequiredArgsConstructor
public class AuthController {
private final ILoginService loginService;
/**
* 登錄
* @param req 請(qǐng)求參數(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.登錄實(shí)現(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) {
//登錄驗(yàn)證
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))
//過(guò)期時(shí)間3小時(shí)
.setExpiresAt(new Date(System.currentTimeMillis() + 1000 * 60 * 60))
.sign();
return token;
}
}到此這篇關(guān)于springSecurity+jwt使用小結(jié)的文章就介紹到這了,更多相關(guān)springSecurity使用jwt內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希
- SpringSecurity+Redis+Jwt實(shí)現(xiàn)用戶認(rèn)證授權(quán)
- springboot+springsecurity+mybatis+JWT+Redis?實(shí)現(xiàn)前后端離實(shí)戰(zhàn)教程
- SpringBoot3.0+SpringSecurity6.0+JWT的實(shí)現(xiàn)
- SpringSecurity整合JWT的使用示例
- SpringBoot整合SpringSecurity和JWT和Redis實(shí)現(xiàn)統(tǒng)一鑒權(quán)認(rèn)證
- SpringBoot+SpringSecurity+jwt實(shí)現(xiàn)驗(yàn)證
- SpringSecurity詳解整合JWT實(shí)現(xiàn)全過(guò)程
- mall整合SpringSecurity及JWT認(rèn)證授權(quán)實(shí)戰(zhàn)下
- mall整合SpringSecurity及JWT實(shí)現(xiàn)認(rèn)證授權(quán)實(shí)戰(zhàn)
- Java SpringSecurity+JWT實(shí)現(xiàn)登錄認(rèn)證
相關(guān)文章
源碼解讀Spring-Integration執(zhí)行過(guò)程
Spring-Integration基于Spring,在應(yīng)用程序中啟用了輕量級(jí)消息傳遞,并支持通過(guò)聲明式適配器與外部系統(tǒng)集成,今天主要是看個(gè)簡(jiǎn)單的hello word進(jìn)來(lái)分析下整個(gè)執(zhí)行過(guò)程,感興趣的朋友一起看看吧2021-06-06
intellij idea14打包apk文件和查看sha1值
這篇文章主要為大家詳細(xì)介紹了intellij idea14打包apk文件和查看sha1值,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-10-10
MyBatis基礎(chǔ)支持DataSource實(shí)現(xiàn)源碼解析
這篇文章主要為大家介紹了MyBatis基礎(chǔ)支持DataSource實(shí)現(xiàn)源碼解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-02-02
Java技能點(diǎn)之SimpleDateFormat進(jìn)行日期格式化問題
這篇文章主要介紹了Java技能點(diǎn)之SimpleDateFormat進(jìn)行日期格式化問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-04-04
解讀Spring配置文件中的property標(biāo)簽中的屬性
這篇文章主要介紹了Spring配置文件中的property標(biāo)簽中的屬性,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-01-01
使用java生成json時(shí)產(chǎn)生棧溢出錯(cuò)誤問題及解決方案
這篇文章主要介紹了使用java生成json時(shí)產(chǎn)生棧溢出錯(cuò)誤問題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-06-06
SpringBoot單元測(cè)試使用@Test沒有run方法的解決方案
這篇文章主要介紹了SpringBoot單元測(cè)試使用@Test沒有run方法的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-01-01
java運(yùn)行時(shí)數(shù)據(jù)區(qū)域和類結(jié)構(gòu)詳解
這篇文章主要介紹了java運(yùn)行時(shí)數(shù)據(jù)區(qū)域和類結(jié)構(gòu),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07

