springSecurity+jwt使用小結
(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)綁定權限校驗失敗類: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,獲取用戶信息,然后設置到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(); } /** * 權限校驗 * @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)) //鑒權失敗類 .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 { // 設置響應狀態(tài)碼為401(未授權) response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); // 設置響應內容類型 response.setContentType("application/json"); response.setCharacterEncoding("UTF-8"); // 響應體內容,可以根據(jù)需要自定義 ResponseDto fail = ResponseDto.fail(RespCodeEnum.ACCESS_DENIED); response.getWriter().write(JSONUtil.toJsonStr(fail)); } }
4.權限認證失敗處理類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; /** * * 權限認證失敗處理 */ @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.自定義權限校驗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)登錄接口和接口權限校驗
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; } }
到此這篇關于springSecurity+jwt使用小結的文章就介紹到這了,更多相關springSecurity使用jwt內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希
- SpringSecurity+Redis+Jwt實現(xià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)一鑒權認證
- SpringBoot+SpringSecurity+jwt實現(xiàn)驗證
- SpringSecurity詳解整合JWT實現(xiàn)全過程
- mall整合SpringSecurity及JWT認證授權實戰(zhàn)下
- mall整合SpringSecurity及JWT實現(xiàn)認證授權實戰(zhàn)
- Java SpringSecurity+JWT實現(xiàn)登錄認證
相關文章
源碼解讀Spring-Integration執(zhí)行過程
Spring-Integration基于Spring,在應用程序中啟用了輕量級消息傳遞,并支持通過聲明式適配器與外部系統(tǒng)集成,今天主要是看個簡單的hello word進來分析下整個執(zhí)行過程,感興趣的朋友一起看看吧2021-06-06intellij idea14打包apk文件和查看sha1值
這篇文章主要為大家詳細介紹了intellij idea14打包apk文件和查看sha1值,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2020-10-10MyBatis基礎支持DataSource實現(xiàn)源碼解析
這篇文章主要為大家介紹了MyBatis基礎支持DataSource實現(xiàn)源碼解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-02-02Java技能點之SimpleDateFormat進行日期格式化問題
這篇文章主要介紹了Java技能點之SimpleDateFormat進行日期格式化問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-04-04使用java生成json時產(chǎn)生棧溢出錯誤問題及解決方案
這篇文章主要介紹了使用java生成json時產(chǎn)生棧溢出錯誤問題及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-06-06SpringBoot單元測試使用@Test沒有run方法的解決方案
這篇文章主要介紹了SpringBoot單元測試使用@Test沒有run方法的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-01-01java運行時數(shù)據(jù)區(qū)域和類結構詳解
這篇文章主要介紹了java運行時數(shù)據(jù)區(qū)域和類結構,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-07-07