穿过你的黑发的眼的我 發表於 2025-5-29 08:45:00

springboot~3.x版本的认证逻辑

<p>在 Spring Boot 3.4.x 中,<code>HttpSecurity</code> 的 <code>and()</code> 方法已经被标记为过时,因此我们需要采用新的 Lambda 风格 API 来配置安全性。你可以将 <code>exceptionHandling()</code> 移到 <code>HttpSecurity</code> 的顶层配置中,而不是在 <code>authorizeHttpRequests</code> 的内部。</p>
<h3 id="authenticationentrypoint和accessdeniedhandler的自定义">authenticationEntryPoint和accessDeniedHandler的自定义</h3>
<pre><code>import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;

import java.io.IOException;

public class CustomAccessDeineHandler implements AccessDeniedHandler {

    @Override
    public void handle(HttpServletRequest request,
                     HttpServletResponse response,
                     AccessDeniedException accessDeniedException) throws IOException {
      response.setContentType("application/json");
      response.setStatus(HttpServletResponse.SC_FORBIDDEN);
      response.getWriter().write("{\"error\": \"forbidden\", \"message\": \"" + accessDeniedException.getMessage() + "\"}");

    }

}

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;

import java.io.IOException;

/**
* 默认的认证入口点,当用户未通过认证时会触发此类,返回401状态码和错误信息。
* @author lind
* @date 2025/5/28 16:59
* @since 1.0.0
*/
public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {
    @Override
    public void commence(HttpServletRequest request,
                         HttpServletResponse response,
                         AuthenticationException authException) throws IOException {
      response.setContentType("application/json");
      response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
      response.getWriter().write("{\"error\": \"Unauthorized\", \"message\": \"" + authException.getMessage() + "\"}");
    }
}
</code></pre>
<h3 id="handlerconfig注册bean">HandlerConfig注册bean</h3>
<pre><code>@Configuration
public class HandlerConfig {
    @Bean
    @ConditionalOnMissingBean
    AuthenticationEntryPoint authenticationEntryPoint() {
      return new CustomAuthenticationEntryPoint();
    }

    @Bean
    @ConditionalOnMissingBean
    public AccessDeniedHandler accessDeniedHandler() {
      return new CustomAccessDeineHandler();
    }
}
</code></pre>
<h3 id="websecurityconfig代码">WebSecurityConfig代码</h3>
<pre><code class="language-java">@EnableWebSecurity
public class WebSecurityConfig {

    private UaaProperty uaaProperty;
    private AuthenticationEntryPoint authenticationEntryPoint;
    private AccessDeniedHandler accessDeniedHandler;

    public WebSecurityConfig(UaaProperty uaaProperty, AuthenticationEntryPoint authenticationEntryPoint, AccessDeniedHandler accessDeniedHandler) {
      this.uaaProperty = uaaProperty;
      this.authenticationEntryPoint = authenticationEntryPoint;
      this.accessDeniedHandler = accessDeniedHandler;
    }

    @Bean
    public JwtAuthenticationFilter jwtAuthenticationFilter() {
      return new JwtAuthenticationFilter(uaaProperty);
    }

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
      Set&lt;String&gt; set = new HashSet&lt;&gt;();
      if (uaaProperty.getPermitUrl() != null &amp;&amp; uaaProperty.getPermitUrl().length &gt; 0) {
            Collections.addAll(set, uaaProperty.getPermitUrl());
      }

      http.csrf(csrf -&gt; csrf.disable())
                .authorizeHttpRequests(authorize -&gt; authorize
                        .requestMatchers(set.toArray(new String[]{})).permitAll()
                        .anyRequest().authenticated()
                )
                .exceptionHandling(exceptionHandling -&gt;
                        exceptionHandling
                              .authenticationEntryPoint(authenticationEntryPoint)
                              .accessDeniedHandler(accessDeniedHandler)
                )
                .addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);

      return http.build();
    }
}
</code></pre>
<h3 id="jwtauthenticationfilter实现自定义验证逻辑">JwtAuthenticationFilter实现自定义验证逻辑</h3>
<pre><code>public class JwtAuthenticationFilter extends OncePerRequestFilter {

    // 配置白名单策略,不走当前doFilterInternal
    @Override
    protected boolean shouldNotFilter(HttpServletRequest request) {
      
    }
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
               
    }
}
</code></pre>
<h3 id="新版3x的spi风格自动装配">新版3.x的SPI风格自动装配</h3>
<ul>
<li>resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports</li>
</ul>
<pre><code>com.xxx.uaa.keycloak.config.UaaProperty
com.xxx.uaa.keycloak.config.WebSecurityConfig
com.xxx.uaa.keycloak.config.HandlerConfig

</code></pre>
<h3 id="关键点解释">关键点解释</h3>
<ol>
<li>
<p><strong>Lambda 风格 API</strong>:使用 <code>exceptionHandling(exceptionHandling -&gt; ...)</code> 的方式来设置 <code>authenticationEntryPoint</code> 和 <code>accessDeniedHandler</code>,这符合新的配置风格,避免了使用过时的方法。</p>
</li>
<li>
<p><strong>结构清晰</strong>:通过这种方法,你的代码结构更加清晰,逻辑分离也更明显。</p>
</li>
<li>
<p><strong>保持原有逻辑</strong>:其余部分的逻辑保持不变,仍然可以根据需要添加其他的配置。</p>
</li>
</ol>
<h3 id="注意事项">注意事项</h3>
<ul>
<li>确保你的 Spring Security 版本已经更新到 5.4 或更高版本,以支持这种新的配置方式。</li>
<li>如果你有其他的异常处理或安全配置,也可以在同一链中继续添加。</li>
</ul>


</div>
<div id="MySignature" role="contentinfo">
    <p></p>
<div class="navgood">
<p>作者:仓储大叔,张占岭,<br>
荣誉:微软MVP<br>QQ:853066980</p>

<p><strong>支付宝扫一扫,为大叔打赏!</strong>
<br><img src="https://images.cnblogs.com/cnblogs_com/lori/237884/o_IMG_7144.JPG"></p>
</div><br><br>
来源:https://www.cnblogs.com/lori/p/18901689
頁: [1]
查看完整版本: springboot~3.x版本的认证逻辑