springboot3.2.8【security登录认证】

2024-08-23 15:28

本文主要是介绍springboot3.2.8【security登录认证】,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

    • 概要
    • 整体架构流程
      • 代码思路
      • 代码展示
    • 小结

概要

springboot3.2.8,使用security登录认证

整体架构流程

代码思路

1.配置WebSecurityConfig类,配置正给http请求的认证和鉴权
2.自定义拦截器customizeAuthenticationFilter(),+账号密码验证UsernamePasswordAuthenticationFilter对用户认证

代码展示

  • WebSecurityConfig 类,这里隐藏了一些内部使用的包。大家用可替换的包和方法函数解决。
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
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.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;/*** 该类定义了SpringSecurity的主要流程配置*/@Configuration
@EnableWebSecurity
@EnableMethodSecurity
//从配置文件中的前缀获取配置
@ConfigurationProperties(prefix = "system")
@Data
public class WebSecurityConfig {//handler定义了很多 通知@Autowiredprivate CustomizeAuthenticationSuccessHandler customizeAuthenticationSuccessHandler;@Autowiredprivate CustomizeAuthenticationFailureHandler customizeAuthenticationFailureHandler;@Autowiredprivate CustomizeLogoutSuccessHandler customizeLogoutSuccessHandler;@Autowired//鉴权token过滤器private JwtAuthenticationToKenFilter jwtAuthenticationToKenFilter;@Autowiredprivate CustomizeAccessDeniedHandler customizeAccessDeniedHandler;@Autowiredprivate CustomizeAuthenticationEntryPoint customizeAuthenticationEntryPoint;@Autowiredprivate UserDetailsService userDetailsService;@Autowiredprivate TokenService tokenService;//定义了白名单private String[] whiteList;@Beanpublic SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws  Exception{httpSecurity//禁用csrf 因为不使用session.csrf(csrf->csrf.disable())//禁用自带的跨域处理.cors(cors->cors.disable())//基于token 关掉session.sessionManagement(session->session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))//权限路径控制.authorizeHttpRequests(request->{request.requestMatchers(whiteList).permitAll()/*.requestMatchers(HttpMethod.GET,"/","/*.html","/**.html","/**.css","/**.js","/profile/**","/grpc/**").permitAll().requestMatchers("/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/webjars/**", "/v3/**").permitAll().requestMatchers("/auth/**").permitAll()*/.anyRequest().authenticated();})//登录,这里将登录注释。依然可以通过POST带参数访问/login/*.formLogin(login->{login//登录接口地址.loginProcessingUrl("/login")//允许所有用户登录.permitAll()//登录成功处理.successHandler(customizeAuthenticationSuccessHandler)//登录失败处理.failureHandler(customizeAuthenticationFailureHandler);})*///注销.logout(logout->{logout.logoutUrl("/logout")//注销成功处理.logoutSuccessHandler(customizeLogoutSuccessHandler);})//配置自定义filter.addFilterAt(customizeAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)//jwt权限过滤
//                .addFilterBefore(jwtAuthenticationToKenFilter, CustomizeAuthenticationFilter.class)//异常处理.exceptionHandling(exception->{exception//认证失败处理.authenticationEntryPoint(customizeAuthenticationEntryPoint)//权限失败处理.accessDeniedHandler(customizeAccessDeniedHandler);});return httpSecurity.build();}/*** 强散列哈希加密实现*/@Beanpublic BCryptPasswordEncoder bCryptPasswordEncoder() {return new BCryptPasswordEncoder();}/*** 自定义filter配置* 对用户信息验证,并设置成功和失败Handler* @return*/@Beanpublic CustomizeAuthenticationFilter customizeAuthenticationFilter(){CustomizeAuthenticationFilter filter = new CustomizeAuthenticationFilter();//必须设置authenticationManagerfilter.setAuthenticationManager(authenticationManager());filter.setAuthenticationSuccessHandler(customizeAuthenticationSuccessHandler);filter.setAuthenticationFailureHandler(customizeAuthenticationFailureHandler);return filter;}/*** 注入userDetailsService和加解密Encoder,userDetailsService回去调用我们的实现类* @return*/@Beanpublic AuthenticationManager authenticationManager(){DaoAuthenticationProvider provider = new DaoAuthenticationProvider();provider.setUserDetailsService(userDetailsService);provider.setPasswordEncoder(bCryptPasswordEncoder());ProviderManager pm = new ProviderManager(provider);return pm;}}
  • customizeAuthenticationFilter自定义拦截器,验证用户信息
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;import java.io.IOException;
import java.util.Map;/*** 自定义filter* 处理json传参* 处理password解码*/
public class CustomizeAuthenticationFilter extends UsernamePasswordAuthenticationFilter {@Overridepublic Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {if (!request.getMethod().equals("POST")) {throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());} else {if(request.getContentType().equals(MediaType.APPLICATION_JSON_VALUE)){Map<String,String> map;UsernamePasswordAuthenticationToken authRequest = null;try{//转换输入的json为map格式map = new ObjectMapper().readValue(request.getInputStream(),Map.class);String username = map.get(getUsernameParameter());username = username != null ? username.trim() : "";String password = map.get(getPasswordParameter());password = password != null ? password : "";//解密String decryptedPassword = RSAEncrypt.decode(password);authRequest = new UsernamePasswordAuthenticationToken(username,decryptedPassword);}catch (IOException e){e.printStackTrace();authRequest = new UsernamePasswordAuthenticationToken("","");}finally {setDetails(request,authRequest);//调用authenticate完成认证return this.getAuthenticationManager().authenticate(authRequest);}}}//不是json格式按照表单登录逻辑处理return super.attemptAuthentication(request, response);}}
  • 自定义实现类UserDetailsServiceImpl,用于查询数据库验证用户
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.server.authorization.AuthorizationContext;
import org.springframework.stereotype.Service;import java.util.List;
import java.util.Set;/***/
@Service
public class UserDetailsServiceImpl implements UserDetailsService {@Autowiredprivate IUserService userService;@Autowiredprivate IRoleService roleService;@Autowiredprivate IPermissionService permissionService;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();wrapper.eq(User::getUserName,username);wrapper.eq(User::getDelFlag, UserStatus.OK);User user = userService.getOne(wrapper);if(StringUtils.isNull(user)){//throw new UsernameNotFoundException("账号不存在");throw new ServiceException("账号不存在");}UsernameContextHolder.setContext(username);//查询角色List<Role> roles = roleService.queryRolesByUser(user.getUserId());//查询权限Set<String> permissions = permissionService.queryPermissionByRole(roles);return new LoginUser().setUserId(user.getUserId()).setUser(user).setRoles(roles).setPermissions(permissions);}
}
  • LoginUser,自定义的实例
import cn.hutool.core.collection.CollectionUtil;
import lombok.Data;
import lombok.experimental.Accessors;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;import java.util.*;/**
需要实现UserDetails */
@Data
@Accessors(chain = true)
public class LoginUser implements UserDetails {private Long userId;private String token;//    user为普通的用户实体类private User user;private List<Role> roles;private Set<String> permissions;@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {List<GrantedAuthority> authorities = new ArrayList<>();if(CollectionUtil.isNotEmpty(roles)){roles.forEach(role->{MyGrantedAuthority authority = new MyGrantedAuthority(role);//redis对SimpleGrantedAuthority反序列化有问题//GrantedAuthority authority = new SimpleGrantedAuthority(role.getRoleKey());authorities.add(authority);});}return authorities;}public static class MyGrantedAuthority implements GrantedAuthority {private Role role;private String roleKey;@Overridepublic String getAuthority() {return Objects.isNull(this.role) ? this.roleKey : "ROLE_"+this.role.getRoleKey();}public MyGrantedAuthority(Role role) {this.role = role;}public MyGrantedAuthority(String roleKey) {this.roleKey = roleKey;}}@Overridepublic String getPassword() {return user.getPassword();}@Overridepublic String getUsername() {return user.getUserName();}@Overridepublic boolean isAccountNonExpired() {return true;}@Overridepublic boolean isAccountNonLocked() {return user.getLockFlag() == FlagStatus.NOT.getCode();}@Overridepublic boolean isCredentialsNonExpired() {return true;}@Overridepublic boolean isEnabled() {return true;}
}
  • jwt token验证
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;import java.io.IOException;/***/
@Component
public class JwtAuthenticationToKenFilter extends OncePerRequestFilter {@Autowiredprivate TokenService tokenService;@Autowiredprivate RedisService redisService;@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {//获取tokenString token = tokenService.getRequestToken(request);//验证token是否有效LoginUser loginUser = tokenService.getUserByToken(token);if(StringUtils.isNotNull(loginUser)){//判断token是否需要续期Long expireTime = redisService.getExpire(CacheConstants.LOGIN_USER_TOKEN_KEY + loginUser.getUserId());if(expireTime>0 && expireTime<=600L){tokenService.refreshToken(loginUser);}}if(StringUtils.isNotNull(loginUser) && StringUtils.isNull(SecurityUtils.getAuthentication())){UsernamePasswordAuthenticationToken authentication =new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());SecurityContextHolder.getContext().setAuthentication(authentication);}filterChain.doFilter(request,response);}
}
  • token的管理
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;
import jakarta.servlet.http.HttpServletRequest;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;@Component
@ConfigurationProperties(prefix = "jwt")
@Data
@Slf4j
public class TokenService {// 令牌自定义标识private String header;// 令牌秘钥private String secret;// 令牌过期时间private Long expireTime;@Autowiredprivate RedisService redisService;/*** 创建令牌** @param loginUser* @return*/public String createToken(LoginUser loginUser) {String token = UUID.randomUUID().toString();loginUser.setToken(token);refreshToken(loginUser);Map<String, Object> claims = new HashMap<>();claims.put(Constants.LOGIN_USER_KEY, token);return createToken(claims);}/*** 从数据声明生成令牌** @param claims* @return*/private String createToken(Map<String, Object> claims) {/*return Jwts.builder().setClaims(claims).setHeaderParam("typ", "JWT").setHeaderParam("alg", "HS256").setSubject("jacob-user").setIssuedAt(new Date()).signWith(SignatureAlgorithm.HS256, secret).compact();*/return Jwts.builder().claims(claims).subject("jacob-user").issuedAt(new Date()).signWith(Keys.hmacShaKeyFor(secret.getBytes()), Jwts.SIG.HS256).compact();}/*** 刷新令牌有效期** @param loginUser 登录信息*/public void refreshToken(LoginUser loginUser) {//缓存(token,user)redisService.setCacheObject(getTokenKey(loginUser.getToken()), loginUser, expireTime, TimeUnit.MINUTES);//缓存(user,token)redisService.setCacheObject(getUserTokenKey(loginUser), loginUser.getToken(), expireTime, TimeUnit.MINUTES);}/*** 删除用户身份信息*/public void deleteToken(LoginUser loginUser) {if (StringUtils.isNotEmpty(loginUser.getToken())) {redisService.deleteObject(getTokenKey(loginUser.getToken()));redisService.deleteObject(getUserTokenKey(loginUser));}}/*** 删除用户身份信息*/public void deleteUserToken(LoginUser loginUser) {if(redisService.hasKey(getUserTokenKey(loginUser))){String token = redisService.getCacheObject(getUserTokenKey(loginUser));redisService.deleteObject(getTokenKey(token));redisService.deleteObject(getUserTokenKey(loginUser));}}private String getTokenKey(String uuid) {return CacheConstants.LOGIN_TOKEN_KEY + uuid;}private String getUserTokenKey(LoginUser loginUser) {return CacheConstants.LOGIN_USER_TOKEN_KEY + loginUser.getUserId();}/*** 获取请求中的token** @param request* @return*/public String getRequestToken(HttpServletRequest request) {String token = request.getHeader(header);if (StringUtils.isNotEmpty(token) && token.startsWith(Constants.TOKEN_PREFIX)) {token = token.replace(Constants.TOKEN_PREFIX, "");}return token;}/*** 获取用户信息** @param token* @return*/public LoginUser getUserByToken(String token) {if (StringUtils.isEmpty(token)) {return null;}try {Claims claims = parseToken(token);// 解析对应的权限以及用户信息String uuid = claims.get(Constants.LOGIN_USER_KEY, String.class);return redisService.getCacheObject(getTokenKey(uuid));} catch (Exception e) {log.error("验证token失败!{}", e.getMessage());}return null;}/*** 从令牌中获取数据声明** @param token* @return*/private Claims parseToken(String token) {/*return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();*/return Jwts.parser().verifyWith(Keys.hmacShaKeyFor(secret.getBytes())).build().parseSignedClaims(token).getPayload();}/**** 清除锁定缓存* @param username*/public void clearLoginRecordCache(String username){if (redisService.hasKey(getPasswordErrorKey(username))) {redisService.deleteObject(getPasswordErrorKey(username));}}private String getPasswordErrorKey(String username){return CacheConstants.PWD_ERR_CNT_KEY + username;}}
  • 成功通知类CustomizeAuthenticationSuccessHandler
import cn.hutool.extra.servlet.JakartaServletUtil;
import eu.bitwalker.useragentutils.UserAgent;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component;import java.io.IOException;/*** 登录成功处理*/
@Component
public class CustomizeAuthenticationSuccessHandler implements AuthenticationSuccessHandler {@Autowiredprivate TokenService tokenService;@Autowiredprivate IUserService userService;@Autowiredprivate SystemConfig systemConfig;@Overridepublic void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {LoginUser loginUser = (LoginUser) authentication.getPrincipal();//登录日志Thread.ofVirtual().start(()->{AsyncFactory.recordLogininfor(loginUser.getUsername(), Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"),UserAgent.parseUserAgentString(request.getHeader("User-Agent")), JakartaServletUtil.getClientIP(request));});//更新登录信息User user = loginUser.getUser();user.setLoginTime(System.currentTimeMillis());userService.updateById(user);//单点登录if (systemConfig.getOnlyOneLogin()) {tokenService.deleteUserToken(loginUser);}//删除锁定缓存tokenService.clearLoginRecordCache(user.getUserName());//生成tokenString token = tokenService.createToken(loginUser);ResponseUtil.responseToken(response,token);}}

小结

整体内容比较简单,主要用于给新手理一下思路,更快的学习使用。

这篇关于springboot3.2.8【security登录认证】的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1099767

相关文章

Spring Security自定义身份认证的实现方法

《SpringSecurity自定义身份认证的实现方法》:本文主要介绍SpringSecurity自定义身份认证的实现方法,下面对SpringSecurity的这三种自定义身份认证进行详细讲解,... 目录1.内存身份认证(1)创建配置类(2)验证内存身份认证2.JDBC身份认证(1)数据准备 (2)配置依

Spring Security+JWT如何实现前后端分离权限控制

《SpringSecurity+JWT如何实现前后端分离权限控制》本篇将手把手教你用SpringSecurity+JWT搭建一套完整的登录认证与权限控制体系,具有很好的参考价值,希望对大家... 目录Spring Security+JWT实现前后端分离权限控制实战一、为什么要用 JWT?二、JWT 基本结构

kali linux 无法登录root的问题及解决方法

《kalilinux无法登录root的问题及解决方法》:本文主要介绍kalilinux无法登录root的问题及解决方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,... 目录kali linux 无法登录root1、问题描述1.1、本地登录root1.2、ssh远程登录root2、

SpringSecurity JWT基于令牌的无状态认证实现

《SpringSecurityJWT基于令牌的无状态认证实现》SpringSecurity中实现基于JWT的无状态认证是一种常见的做法,本文就来介绍一下SpringSecurityJWT基于令牌的无... 目录引言一、JWT基本原理与结构二、Spring Security JWT依赖配置三、JWT令牌生成与

SpringSecurity6.0 如何通过JWTtoken进行认证授权

《SpringSecurity6.0如何通过JWTtoken进行认证授权》:本文主要介绍SpringSecurity6.0通过JWTtoken进行认证授权的过程,本文给大家介绍的非常详细,感兴趣... 目录项目依赖认证UserDetailService生成JWT token权限控制小结之前写过一个文章,从S

Spring Security基于数据库的ABAC属性权限模型实战开发教程

《SpringSecurity基于数据库的ABAC属性权限模型实战开发教程》:本文主要介绍SpringSecurity基于数据库的ABAC属性权限模型实战开发教程,本文给大家介绍的非常详细,对大... 目录1. 前言2. 权限决策依据RBACABAC综合对比3. 数据库表结构说明4. 实战开始5. MyBA

Spring Security方法级安全控制@PreAuthorize注解的灵活运用小结

《SpringSecurity方法级安全控制@PreAuthorize注解的灵活运用小结》本文将带着大家讲解@PreAuthorize注解的核心原理、SpEL表达式机制,并通过的示例代码演示如... 目录1. 前言2. @PreAuthorize 注解简介3. @PreAuthorize 核心原理解析拦截与

springboot security使用jwt认证方式

《springbootsecurity使用jwt认证方式》:本文主要介绍springbootsecurity使用jwt认证方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地... 目录前言代码示例依赖定义mapper定义用户信息的实体beansecurity相关的类提供登录接口测试提供一

springboot security验证码的登录实例

《springbootsecurity验证码的登录实例》:本文主要介绍springbootsecurity验证码的登录实例,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,... 目录前言代码示例引入依赖定义验证码生成器定义获取验证码及认证接口测试获取验证码登录总结前言在spring

springboot security快速使用示例详解

《springbootsecurity快速使用示例详解》:本文主要介绍springbootsecurity快速使用示例,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝... 目录创www.chinasem.cn建spring boot项目生成脚手架配置依赖接口示例代码项目结构启用s