云飞起 發表於 2025-5-5 16:42:00

自定义Spring Authorization Server登录页

<h2 id="一鸣谢">一、鸣谢</h2>
<blockquote>
<p>首先要声明一些感谢:</p>
<ol>
<li>感谢官方文档的缺失、反复造成我下面这条感谢</li>
<li>感谢那些胡说八道、顾彼失此的某DN文章,让我在冲向坑里的道路上一往无前</li>
</ol>
</blockquote>
<p>废话不多说,看剑!</p>
<p>本文来自:博客园-去哪里吃鱼-自定义Spring Authorization Server登录页</p>
<h2 id="二版本信息">二、版本信息</h2>
<p>本文基于如下以来版本信息,官方代码如有变动,请自行阅读源码解决问题。</p>
<p>友情提示:<font color="red"><strong>不要照抄某DN、AI内容,避免浪费生命。</strong> </font></p>
<pre><code class="language-xml">&lt;dependency&gt;
        &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
        &lt;artifactId&gt;spring-boot-starter-oauth2-authorization-server&lt;/artifactId&gt;
        &lt;version&gt;3.3.10&lt;/version&gt;
&lt;/dependency&gt;

&lt;!-- 上面的依赖引用的 spring-security-oauth2-authorization-server 版本,这里只做提示,不用引入 --&gt;
&lt;dependency&gt;
        &lt;groupId&gt;org.springframework.security&lt;/groupId&gt;
        &lt;artifactId&gt;spring-security-oauth2-authorization-server&lt;/artifactId&gt;
        &lt;version&gt;1.3.5&lt;/version&gt;
&lt;/dependency&gt;
</code></pre>
<h2 id="三过滤器链介绍">三、过滤器链介绍</h2>
<p>使用 <code>Spring Authorization Server</code> 开发授权服务器,必然不可少要配置两个 <code>SecurityFilterChain</code>过滤器链:</p>
<ul>
<li><code>SpringSecurity</code>的过滤器链</li>
<li>授权服务的过滤器链</li>
</ul>
<p>这两条过滤器链不会冲突,但是要确保授权服务器的过滤器链在SpringSecurity链之前加载,在代码当中可以使用<code>@Order(0)</code>注解来调整,注解中的<code>整数参数</code>越小,加载顺序就越靠前</p>
<p>调整的目的是:</p>
<p><code>SpringSecurity</code>的过滤器链默认所有请求都需要认证,把授权服务的过滤器链提前,可以避免如下默认授权相关请求不受<code>SpringSecurity</code>的过滤器链影响</p>
<ul>
<li>/oauth2/authorize</li>
<li>/oauth2/token</li>
<li>/oauth2/jwks</li>
<li>/userinfo</li>
<li>/login</li>
<li>...</li>
</ul>
<p>如下基于 <code>授权码</code> 模式进行开发,默认的,以 <code>GET</code> 方式请求的 <code>/login</code> 是跳转到登录页,以 <code>POST</code> 方式请求的 <code>/login</code> 则是处理登录请求,Spring 官方以硬编码的形式提供了一个默认登录页,其中引用了<code>bootstrap</code> 样式文件,鉴于一些众所周知的原因,这个样式文件访问不了,所以登录页加载会很慢。</p>
<p>此外,每个产品也会相应的设计具有自己产品风格特性的登录页,这让自定义登录页成为了一个硬性需求。</p>
<p>如下为 <code>只修改登录页</code> 的处理过程,一些项目配置如下</p>
<ul>
<li>应用 context-path 自定义为 'auth'</li>
<li>自定义登录页请求地址为: http://domain/auth/i/login</li>
</ul>
<h2 id="四修改-springsecurity-配置">四、修改 SpringSecurity 配置</h2>
<p>贴一段基于官方demo的修改后的配置代码:</p>
<pre><code class="language-java">@Bean
@Order(2)
public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http)
        throws Exception {
        http
                .authorizeHttpRequests((authorize) -&gt; authorize
                        .requestMatchers("/i/**","/login**").permitAll()
                        .anyRequest().authenticated()
                )
                .cors(AbstractHttpConfigurer::disable)
                .csrf(AbstractHttpConfigurer::disable)
                .formLogin(form -&gt; form.loginPage("/i/login").loginProcessingUrl("/login"));
        return http.build();
}
</code></pre>
<p>现在来说一下注意事项:</p>
<ol>
<li>登录页请求由<code>SpringSecurity</code>的过滤器链处理,因为授权服务过滤器链没有设置 <code>formLogin</code>,它只处理相关接口</li>
<li>在配置 <code>formLogin</code> 的时候,<code>loginPage</code> 和 <code>loginProcessingUrl</code> 都需要配置,如果不配置 <code>loginProcessingUrl</code>,它则会用 <code>loginPage</code> 的 url来处理登录请求</li>
<li>除了登录页请求放开,登录请求也要放开,交由授权服务过滤器链处理,在代码中就是 <code>requestMatchers("/i/**","/login**").permitAll()</code></li>
</ol>
<h2 id="五修改-spring-authorization-server-配置">五、修改 Spring Authorization Server 配置</h2>
<p>同样基于官方demo的修改后的配置:</p>
<pre><code class="language-java">@Bean
@Order(1)
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
        OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
        http.getConfigurer(OAuth2AuthorizationServerConfigurer.class).oidc(withDefaults());
        http.exceptionHandling((exceptions) -&gt; exceptions
                .defaultAuthenticationEntryPointFor(
                        new LoginUrlAuthenticationEntryPoint(domain + "/auth/i/login"),
                        new MediaTypeRequestMatcher(MediaType.TEXT_HTML)
                )
        )
        .oauth2ResourceServer((resourceServer) -&gt; resourceServer.jwt(Customizer.withDefaults()));
        return http.build();
}
</code></pre>
<p>注意事项之在代码 <code>new LoginUrlAuthenticationEntryPoint(domain + "/auth/i/login")</code> 中</p>
<ol>
<li><code>domain</code> 变量仅在有需要的情况下使用,也可以不用</li>
<li>构造函数中的地址,<font color="red">一定一定</font>要与上一章节中的 <code>loginPage</code> 地址相同,不要被 <code>xxxEntryPoint</code> 迷惑,这里就是指登录页地址!</li>
</ol>
<h2 id="六其他">六、其他</h2>
<p>自定义授权确认页面不在此篇幅范围之内,这个版本当客户端请求的 <code>role</code> 是一个值的时候不会出现授权确认页面。</p>
<p>我解决这种自定义问题的思路:</p>
<blockquote>
<p>研究 Spring Security 的配置方式,如:<code>SecurityBuilder</code>,<code>SecurityConfiger</code>,从而找到自己出现问题所在的步骤,针对性的去调整</p>
</blockquote>
<p>看到这里,希望对你有所帮助。</p><br><br>
来源:https://www.cnblogs.com/qnlcy/p/18860158
頁: [1]
查看完整版本: 自定义Spring Authorization Server登录页