0


SpringBoot最新版本Security配置(2023年),亲测成功

SpringBoot最新版本Security配置(2023年),亲测成功

pom依赖:

<?xml version="1.0" encoding="UTF-8"?><projectxmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.0.6</version><relativePath/><!-- lookup parent from repository --></parent><groupId>com.yw</groupId><artifactId>reviewTest</artifactId><version>0.0.1-SNAPSHOT</version><name>reviewTest</name><description>reviewTest</description><properties><java.version>17</java.version></properties><dependencies><!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-jdbc --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId><version>3.0.2</version></dependency><!-- https://mvnrepository.com/artifact/org.projectlombok/lombok --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.26</version><scope>provided</scope></dependency><!-- https://mvnrepository.com/artifact/com.mysql/mysql-connector-j --><dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId><version>8.0.32</version></dependency><!-- https://mvnrepository.com/artifact/com.baomidou/mybatis-plus-boot-starter --><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.3</version></dependency><!-- https://mvnrepository.com/artifact/com.baomidou/mybatis-plus-generator --><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-generator</artifactId><version>3.5.3.1</version></dependency><!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-security --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId><version>3.0.2</version></dependency><!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-api --><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-api</artifactId><version>0.11.5</version></dependency><!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-impl --><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-impl</artifactId><version>0.11.5</version><scope>runtime</scope></dependency><!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-jackson --><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-jackson</artifactId><version>0.11.5</version><scope>runtime</scope></dependency><!-- https://mvnrepository.com/artifact/org.jetbrains/annotations --><dependency><groupId>org.jetbrains</groupId><artifactId>annotations</artifactId><version>23.0.0</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId><version>3.0.6</version></dependency><dependency><groupId>com.alibaba.fastjson2</groupId><artifactId>fastjson2</artifactId><version>2.0.29</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><excludes><exclude><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></exclude></excludes></configuration></plugin></plugins></build></project>

application.properties

server.port=3000
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.url=jdbc:mysql://localhost:3307/kob?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

SecurityConfig:

packagecom.yw.reviewtest.config;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importorg.springframework.http.HttpMethod;importorg.springframework.security.authentication.AuthenticationManager;importorg.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;importorg.springframework.security.config.annotation.web.builders.HttpSecurity;importorg.springframework.security.config.annotation.web.configuration.EnableWebSecurity;importorg.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;importorg.springframework.security.config.http.SessionCreationPolicy;importorg.springframework.security.config.ldap.LdapBindAuthenticationManagerFactory;importorg.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;importorg.springframework.security.crypto.password.PasswordEncoder;importorg.springframework.security.web.SecurityFilterChain;importorg.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;importorg.springframework.web.cors.CorsConfiguration;importorg.springframework.web.cors.CorsConfigurationSource;importorg.springframework.web.cors.UrlBasedCorsConfigurationSource;importorg.springframework.web.filter.CorsFilter;/**
 * @author yw~
 * @version 1.0
 */@Configuration@EnableWebSecuritypublicclassSecurityConfig{@AutowiredprivateJwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;// jwt 校验过滤器,从 http 头部 Authorization 字段读取 token 并校验@BeanpublicJwtAuthenticationTokenFilterauthFilter()throwsException{returnnewJwtAuthenticationTokenFilter();}@BeanpublicPasswordEncoderpasswordEncoder(){returnnewBCryptPasswordEncoder();}/**
     * 获取AuthenticationManager(认证管理器),登录时认证使用
     *
     * @param authenticationConfiguration
     * @return
     * @throws Exception
     */@BeanpublicAuthenticationManagerauthenticationManager(AuthenticationConfiguration authenticationConfiguration)throwsException{return authenticationConfiguration.getAuthenticationManager();}@BeanpublicSecurityFilterChainfilterChain(HttpSecurity http)throwsException{
        http
                // 基于 token,不需要 csrf.csrf().disable()// 基于 token,不需要 session.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()// 前后端分离架构不需要csrf保护.authorizeHttpRequests(authorizeHttpRequests -> authorizeHttpRequests
                        // 允许所有OPTIONS请求.requestMatchers(HttpMethod.OPTIONS,"/**").permitAll()// 允许直接访问授权登录接口//一般浏览器访问url为get请求,而前端请求设置中为post,增加安全.requestMatchers(HttpMethod.POST,"/user/account/token/").permitAll()// 允许 SpringMVC 的默认错误地址匿名访问.requestMatchers("/error").permitAll()// 其他所有接口必须有Authority信息,Authority在登录成功后的UserDetailsImpl对象中默认设置“ROLE_USER”//.requestMatchers("/**").hasAnyAuthority("ROLE_USER")// 所有请求将会拦截,除了已经登录.anyRequest().authenticated());//这里默认为security登录页面,在后续需要关闭时进行// 添加 JWT 过滤器,JWT 过滤器在用户名密码认证过滤器之前
       http.addFilterBefore(authFilter(),UsernamePasswordAuthenticationFilter.class);//使配置生效return http.build();}@BeanpublicWebSecurityCustomizerwebSecurityCustomizer(){// 将前端请求全双工放行//使用WebSecurity.ignoring()忽略某些URL请求,这些请求将被Spring Security忽略,这意味着这些URL将有受到 CSRF、XSS、Clickjacking 等攻击的可能。return(web)-> web.ignoring().requestMatchers("/websocket/**");}/**
     * 配置跨源访问(CORS)
     * @return
     */@BeanpublicCorsFiltercorsFilter(){UrlBasedCorsConfigurationSource source =newUrlBasedCorsConfigurationSource();CorsConfiguration corsConfiguration =newCorsConfiguration();
        corsConfiguration.addAllowedOriginPattern("*");
        corsConfiguration.addAllowedHeader("*");
        corsConfiguration.addAllowedMethod("*");
        corsConfiguration.setAllowCredentials(true);
        source.registerCorsConfiguration("/**", corsConfiguration);returnnewCorsFilter(source);}}

配置默认登录用户(即用数据库的用户和密码登录):

1:UserDetailsServiceImpl

packagecom.yw.reviewtest.service.impl;importcom.baomidou.mybatisplus.core.conditions.query.QueryWrapper;importcom.yw.reviewtest.mapper.UserMapper;importcom.yw.reviewtest.pojo.User;importcom.yw.reviewtest.utils.UserDetailsImpl;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.security.core.userdetails.UserDetails;importorg.springframework.security.core.userdetails.UserDetailsService;importorg.springframework.security.core.userdetails.UsernameNotFoundException;importorg.springframework.stereotype.Service;/**
 * @author yw~
 * @version 1.0
 */@ServicepublicclassUserDetailsServiceImplimplementsUserDetailsService{@AutowiredprivateUserMapper userMapper;@OverridepublicUserDetailsloadUserByUsername(String username)throwsUsernameNotFoundException{QueryWrapper<User> queryWrapper =newQueryWrapper();
        queryWrapper.eq("username", username);User user = userMapper.selectOne(queryWrapper);if(user ==null){thrownewUsernameNotFoundException("用户不存在");}returnnewUserDetailsImpl(user);}}

2:UserDetailsImpl:

packagecom.yw.reviewtest.utils;importcom.yw.reviewtest.pojo.User;importlombok.AllArgsConstructor;importlombok.Data;importlombok.NoArgsConstructor;importorg.springframework.security.core.GrantedAuthority;importorg.springframework.security.core.userdetails.UserDetails;importjava.util.Collection;/**
 * @author yw~
 * @version 1.0
 */@Data@AllArgsConstructor@NoArgsConstructorpublicclassUserDetailsImplimplementsUserDetails{privateUser user;@OverridepublicCollection<?extendsGrantedAuthority>getAuthorities(){returnnull;}@OverridepublicStringgetPassword(){return user.getPassword();}@OverridepublicStringgetUsername(){return user.getUsername();}@OverridepublicbooleanisAccountNonExpired(){returntrue;}@OverridepublicbooleanisAccountNonLocked(){returntrue;}@OverridepublicbooleanisCredentialsNonExpired(){returntrue;}@OverridepublicbooleanisEnabled(){returntrue;}}

3:配置完,记得更改SecurityConfig配置,添加前端密码加密验证,

因为security默认密码加密为{noop}+密码

在用户进行用户认证登录的时候,前端传来的密码会用BCrpt加密方式进行加密,加密后的密码格式是{id}password,然后程序员会先获取到加密方式也即是{id},假设没有写passwordEncoder(new BCryptPasswordEncoder())那么前端传来的值就不会用BCrpt加密方式进行加密,所以程序员获取前端传来的值时是寻找不到密码中的{id}也即是加密方式的,因此就会报There is no PasswordEcoder mapped for the id "null"异常。

@BeanpublicPasswordEncoderpasswordEncoder(){returnnewBCryptPasswordEncoder();}

jwt:

第一次前端登录,调取后端接口,发送账号和密码,

后端接收到判断和数据库信息是否相同,相同则根据用户或者用户id生成一个jwt返回给前端,

前端接收到jwt存储到本地并跳转相应页面

前端每次跳转页面时都会携带该jwt发送给后端,后端都将对该jwt进行解析

解析成对应用户id来获得相应权限,jwt验证成功就返回给前端数据

如果jwt解析过期或者失败,就返回前端401,前端接收到就清除jwt然后返回登录页面

这里就可以使用security自带登录对数据库信息进行验证登录了

JWT配置:

jwtUtil:

通过该工具类根据用户id生成jwt

//将userId封装成JWTtoken
String jwt= JwtUtil.createJWT(user.getId().toString());
// utils.JwtUtil//为jwt工具类,用来创建、解析 jwt_token//作用是将我们的字符串加上密钥加上有效期变成一个加密后的字符串//另外一个作用是给我们一个令牌让我们把她的有效期解析出来packagecom.yw.reviewtest.utils;importio.jsonwebtoken.Claims;importio.jsonwebtoken.JwtBuilder;importio.jsonwebtoken.Jwts;importio.jsonwebtoken.SignatureAlgorithm;importorg.springframework.stereotype.Component;importjavax.crypto.SecretKey;importjavax.crypto.spec.SecretKeySpec;importjava.util.Base64;importjava.util.Date;importjava.util.UUID;@ComponentpublicclassJwtUtil{// 有效期14天publicstaticfinallong JWT_TTL =60*60*1000L*24*14;// 密钥,内容是随机字符串,长度必须足够长,只能是大小写英文和数字publicstaticfinalString JWT_KEY ="SDFGjhdsfalshdfHFdsjkdsfds121232131afasdfac";//publicstaticStringgetUUID(){return UUID.randomUUID().toString().replaceAll("-","");}publicstaticStringcreateJWT(String subject){JwtBuilder builder =getJwtBuilder(subject,null,getUUID());return builder.compact();}privatestaticJwtBuildergetJwtBuilder(String subject,Long ttlMillis,String uuid){SignatureAlgorithm signatureAlgorithm =SignatureAlgorithm.HS256;SecretKey secretKey =generalKey();long nowMillis =System.currentTimeMillis();Date now =newDate(nowMillis);if(ttlMillis ==null){
            ttlMillis =JwtUtil.JWT_TTL;}long expMillis = nowMillis + ttlMillis;Date expDate =newDate(expMillis);returnJwts.builder().setId(uuid).setSubject(subject).setIssuer("sg").setIssuedAt(now).signWith(signatureAlgorithm, secretKey).setExpiration(expDate);}publicstaticSecretKeygeneralKey(){byte[] encodeKey =Base64.getDecoder().decode(JwtUtil.JWT_KEY);returnnewSecretKeySpec(encodeKey,0, encodeKey.length,"HmacSHA256");}publicstaticClaimsparseJWT(String jwt)throwsException{SecretKey secretKey =generalKey();returnJwts.parserBuilder().setSigningKey(secretKey).build().parseClaimsJws(jwt).getBody();}}

JwtAuthenticationTokenFilter:

客户端的请求头里携带了 token,服务端肯定是需要针对每次请求解析校验 token 的,所以必须得定义一个过滤器,也就是 JwtAuthenticationTokenFilter:

  • 从请求头中获取 token
  • 对 token 进行解析、验签、校验过期时间
  • 校验成功,将验证结果放到 ThreadLocal 中,供下次请求使用
packagecom.yw.reviewtest.config;importcom.yw.reviewtest.mapper.UserMapper;importcom.yw.reviewtest.pojo.User;importcom.yw.reviewtest.utils.JwtUtil;importcom.yw.reviewtest.utils.UserDetailsImpl;importio.jsonwebtoken.Claims;importjakarta.servlet.FilterChain;importjakarta.servlet.ServletException;importjakarta.servlet.http.HttpServletRequest;importjakarta.servlet.http.HttpServletResponse;importorg.jetbrains.annotations.NotNull;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.security.authentication.UsernamePasswordAuthenticationToken;importorg.springframework.security.core.context.SecurityContextHolder;importorg.springframework.stereotype.Component;importorg.springframework.util.StringUtils;importorg.springframework.web.filter.OncePerRequestFilter;importjava.io.IOException;/**
 * @author yw~
 * @version 1.0
 */@ComponentpublicclassJwtAuthenticationConfigextendsOncePerRequestFilter{@AutowiredprivateUserMapper userMapper;@OverrideprotectedvoiddoFilterInternal(HttpServletRequest request,@NotNullHttpServletResponse response,@NotNullFilterChain filterChain)throwsServletException,IOException{String token = request.getHeader("Authorization");if(!StringUtils.hasText(token)||!token.startsWith("Bearer ")){
            filterChain.doFilter(request, response);return;}

        token = token.substring(7);String userid;try{Claims claims =JwtUtil.parseJWT(token);
            userid = claims.getSubject();}catch(Exception e){thrownewRuntimeException(e);}User user = userMapper.selectById(Integer.parseInt(userid));if(user ==null){thrownewRuntimeException("用户名未登录");}UserDetailsImpl loginUser =newUserDetailsImpl(user);UsernamePasswordAuthenticationToken authenticationToken =newUsernamePasswordAuthenticationToken(loginUser,null,null);SecurityContextHolder.getContext().setAuthentication(authenticationToken);

        filterChain.doFilter(request, response);}}

跨域问题:

这里使用在Securitty里面配置了

标签: spring boot java 后端

本文转载自: https://blog.csdn.net/ns3405453840/article/details/130646351
版权归原作者 大三开学菜鸟 所有, 如有侵权,请联系我们删除。

“SpringBoot最新版本Security配置(2023年),亲测成功”的评论:

还没有评论