Spring Security_2

9.4 認證用戶

最簡單的Spring Security配置的話,那么就能無償?shù)氐玫揭粋€登錄頁。實際上,在重寫configure(HttpSecurity)之前,我們都能使用一個簡單卻功能完備的登錄頁。但是,一旦重寫了configure(HttpSecurity)方法,就失去了這個簡單的登錄頁面。

不過,把這個功能找回來也很容易。我們所需要做的就是在configure(HttpSecurity)方法中,調(diào)用formLogin(),如下面的程序清單所示。 請注意,和前面一樣,這里調(diào)用add()方法來將不同的配置指令連接在一起。 如果我們訪問應(yīng)用的“/login”鏈接或者導(dǎo)航到需要認證的頁面,那么將會在瀏覽器中展現(xiàn)登錄頁面。

// 啟用默認的登錄頁
httpSecurity.formLogin()
 .and()
 .authorizeRequests()
 .antMatchers("/spitter/me").hasRole("SPITTER")
 .anyRequest().permitAll()
 .and()
 .requiresChannel()
 .antMatchers("/spitter/from")
 .requiresSecure(); 

9.4.1 添加自定義的登錄頁

需要注意的一個關(guān)鍵點是<form>提交到了什么地方。同時還需要注意username和password輸入域,在你的登錄頁中,需要同樣的輸入域。最后,假設(shè)沒有禁用CSRF的話,還需要保證包含了值為CSRF token的“_csrf”輸入域。

<input type="hidden" name="_csrf" value="6984_hsd2_wrwqc_wrqd" />

在Thymeleaf模板中,包含了username和password輸入域,就像默認的登錄頁一樣,它也提交到了相對于上下文的“/login”頁面上。因為這是一個Thymeleaf模板,因此隱藏的“_csrf”域?qū)詣犹砑拥奖韱沃?/p>

?


<form method="POST" style="box-sizing: border-box; margin-top: 0px; margin-bottom: 0px;">

9.4.2 啟用HTTP Basic認證</form>

?

參考 HTTP Basic 認證 https://blog.csdn.net/yhb241/article/details/80646485

對于應(yīng)用程序的人類用戶來說,基于表單的認證是比較理想的。但是在第16章中,將會看到如何將我們Web應(yīng)用的頁面轉(zhuǎn)化為RESTful API。當應(yīng)用程序的使用者是另外一個應(yīng)用程序的話,使用表單來提示登錄的方式就不太適合了。

HTTP Basic認證(HTTP Basic Authentication)會直接通過HTTP請求本身,對要訪問應(yīng)用程序的用戶進行認證。你可能在以前見過HTTP Basic認證。當在Web瀏覽器中使用時,它將向用戶彈出一個簡單的模態(tài)對話框。

但這只是Web瀏覽器的顯示方式。本質(zhì)上,這是一個HTTP 401響應(yīng),表明必須要在請求中包含一個用戶名和密碼。在REST客戶端向它使用的服務(wù)進行認證的場景中,這種方式比較適合。

如果要啟用HTTP Basic認證的話,只需在configure()方法所傳入的HttpSecurity對象上調(diào)用httpBasic()即可。另外,還可以通過調(diào)用realmName()方法指定域。如下是在Spring Security中啟用HTTP Basic認證的典型配置:

httpSecurity.formLogin()
 .and()
 .httpBasic()
 .realmName("Spittr")

在httpBasic()方法中,并沒有太多的可配置項,甚至不需要什么額外配置。HTTP Basic認證要么開啟要么關(guān)閉。所以,與其進一步研究這個話題,還不如看看如何通過Remember-me功能實現(xiàn)用戶的自動認證。

9.4.3 啟用Remember-me功能

對于應(yīng)用程序來講,能夠?qū)τ脩暨M行認證是非常重要的。但是站在用戶的角度來講,如果應(yīng)用程序不用每次都提示他們登錄是更好的。這就是為什么許多站點提供了Remember-me功能,你只要登錄過一次,應(yīng)用就會記住你,當再次回到應(yīng)用的時候你就不需要登錄了。

Spring Security使得為應(yīng)用添加Remember-me功能變得非常容易。為了啟用這項功能,只需在configure()方法所傳入的HttpSecurity對象上調(diào)用rememberMe()即可。

httpSecurity.formLogin()
 .loginPage("/login")
 .and()
 .rememberMe()
 .tokenValiditySeconds(2439800)
 .key("spittrKey")
 ....

在這里,我們通過一點特殊的配置就可以啟用Remember-me功能。默認情況下,這個功能是通過在cookie中存儲一個token完成的,這個token最多兩周內(nèi)有效。但是,在這里,我們指定這個token最多四周內(nèi)有效2,419,200秒)。

存儲在cookie中的token包含用戶名、密碼、過期時間和一個私鑰——在寫入cookie前都進行了MD5哈希。默認情況下,私鑰的名為SpringSecured,但在這里我們將其設(shè)置為spitterKey,使它專門用于Spittr應(yīng)用。

如此簡單。既然Remember-me功能已經(jīng)啟用,我們需要有一種方式來讓用戶表明他們希望應(yīng)用程序能夠記住他們。為了實現(xiàn)這一點,登錄請求必須包含一個名為remember-me的參數(shù)。在登錄表單中,增加一個簡單復(fù)選框就可以完成這件事情:

<input id="remember_me" name="remember-me" type="checkbox"/>
<label for="remember_me" class="inline">Remember me</label>

在應(yīng)用中,與登錄同等重要的功能就是退出。如果你啟用Remember-me功能的話,更是如此,否則的話,用戶將永遠登錄在這個系統(tǒng)中。我們下面將看一下如何添加退 出功能。

9.4.4 退出

其實,按照我們的配置,退出功能已經(jīng)啟用了,不需要再做其他的配置了。我們需要的只是一個使用該功能的鏈接。退出功能是通過Servlet容器中的Filter實現(xiàn)的(默認情況下),這個Filter會攔截針對“/logout”的請求。

因此,為應(yīng)用添加退出功能只需添加如下的鏈接即可(如下以Thymeleaf代碼片段的形式進行了展現(xiàn)):

<a th:href="@{/logout}">Logout</a>

當用戶點擊這個鏈接的時候,會發(fā)起對“/logout”的請求,這個請求會被Spring Security的LogoutFilter所處理。用戶會退出應(yīng)用,所有的Remember-me token都會被清除掉。

在退出完成后,用戶瀏覽器將會重定向到“/login?logout”,從而允許用戶進行再次登錄。

如果你希望用戶被重定向到其他的頁面,如應(yīng)用的首頁,那么可以在configure()中進行如下的配置

httpSecurity.formLogin()
 .loginPage("/login")
 .and()
 .logout()
 .logoutSuccessUrl("/")

logout()提供了配置退出行為的方法。在本例中,調(diào)用logoutSuccessUrl()表明在退出成功之后,瀏覽器需要重定 向到“/”。

除了logoutSuccessUrl()方法以外,你可能還希望重寫默認的LogoutFilter攔截路徑。我們可以通過調(diào)用logoutUrl()方法實現(xiàn)這一功能:

.logout()
.logoutSuccessUrl("/")
.logoutUrl("/signout")

如何在發(fā)起請求的時候保護Web應(yīng)用。這假設(shè)安全性主要涉及阻止用戶訪問沒有權(quán)限的URL。

但是,如果我們能夠不給用戶顯示其無權(quán)訪問的連接,那么這也是一個很好的思路。

9.5 保護視圖

當為瀏覽器渲染HTML內(nèi)容時,你可能希望視圖中能夠反映安全限制和相關(guān)的信息。一個簡單的樣例就是渲染用戶的基本信息(比如顯示“您已經(jīng)以……身份登錄”)。或者你想根據(jù)用戶被授予了什么權(quán)限,有條件地渲染特定的視圖元素。 在第6章,我們看到了在Spring MVC應(yīng)用中渲染視圖的兩個最重要的可選方案:JSP和Thymeleaf。不管你使用哪種方案,都有辦法在視圖上實現(xiàn)安全性。Spring Security本身提供了一個JSP標簽庫,而Thymeleaf通過特定的方言實現(xiàn)了與Spring Security的集成。 讓我們看一下如何將Spring Security用到視圖中,就從Spring Security的JSP標簽庫開始吧。

9.5.2 使用Thymeleaf的Spring Security方言

Thymeleaf的安全方言提供了條件化渲染和顯示認證細節(jié)的能力。

為了使用安全方言,我們需要確保Thymeleaf Extras Spring Security已經(jīng)位于應(yīng)用的類路徑下。 然后,還需要在配置中使用SpringTemplateEngine來注冊SpringSecurityDialect。

程序清單9.10所展現(xiàn)的@Bean方法聲明了SpringTemplateEngine bean,其中就包含了SpringSecurityDialect。

@Bean
public SpringTemplateEngine templateEngine(TemplateResolver templateResolver) {
 SpringTemplateEngine templateEngine = new SpringTemplateEngine();
 templateEngine.setTemplateResolver(templateResolver);
 // 注冊安全方言
 templateEngine.addDialect(new SpringStandardDialect());
 return templateEngine;
}

安全方言注冊完成之后,我們就可以在Thymeleaf模板中使用它的屬性了。首先,需要在使用這些屬性的模板中聲明安全命名空間:

<html xmlns="http://www.w3.org/1999/xhtml"
 xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
 ...
</html>

標準的Thymeleaf方法依舊與之前一樣,使用th前綴,安全方言則設(shè)置為使用sec前綴。

這樣我們就能在任意合適的地方使用Thymeleaf屬性了。比如,假設(shè)我們想要為認證用戶渲染“Hello”文本。如下的Thymeleaf模板代碼片段就能完成這項任務(wù):

<div sec:authorize="isAuthenticated()">
 Hello <span sec:authentication="name">someone</span>
</div>

好像沒有效果。。。。

9.6 小結(jié)

對于許多應(yīng)用而言,安全性都是非常重要的切面。Spring Security提供了一種簡單、靈活且強大的機制來保護我們的應(yīng)用程序。

借助于一系列Servlet Filter,Spring Security能夠控制對Web資源的訪問,包括Spring MVC控制器。借助于Spring Security的Java配置模型,我們不必直接處理Filter,能夠非常簡潔地聲明Web安全性功能。

當認證用戶時,Spring Security提供了多種選項。我們探討了如何基于內(nèi)存用戶庫、關(guān)系型數(shù)據(jù)庫和LDAP目錄服務(wù)器來配置認證功能。如果這些可選方案無法滿足認證需求的話,我們還學(xué)習(xí)了如何創(chuàng)建和配置自定義的用戶服務(wù)。

附:參考內(nèi)容:

  1. 手工配置springboot + spring security + thymeleaf + thymeleaf-extras-springsecurity https://my.oschina.net/kitos/blog/1632381

  2. Spring-Security自定義登錄頁&inMemoryAuthentication驗證 https://www.cnblogs.com/MrSi/p/7993875.html>

  3. springboot 構(gòu)建 security https://docs.spring.io/spring-security/site/docs/5.0.13.BUILD-SNAPSHOT/reference/htmlsingle/

  4. 初識 Spring Security https://www.w3cschool.cn/springsecurity/

  5. 參考項目 springboot整合 https://github.com/wean2016/springsecurity

  6. 記住賬號 http://www.itdecent.cn/p/6537deeac03d

  7. JWT的Java使用 https://blog.csdn.net/qq_37636695/article/details/79265711

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9

eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9 eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.

eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.

TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

=================================================================

實戰(zhàn)代碼


基礎(chǔ)的自定義頁面認證

import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
?
/**
 * @description:  繼承AbstractSecurityWebApplicationInitializer會自動注冊DelegatingFilterProxy
 *  等價于xml配置 springSecurityFilterChain
 * @version: 1.0
 * @data: 2019-04-19 11:52
 */
public class SecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer {
}
package com.web.spittr.config.security;
?
import com.web.spittr.data.SpittleRepository;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
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.configuration.WebSecurityConfigurerAdapter;
?
import org.springframework.security.config.annotation.web.servlet.configuration.EnableWebMvcSecurity;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
?
import javax.sql.DataSource;
?
?
@Configuration
@EnableWebMvcSecurity
//@EnableWebSecurity
@Slf4j
public class SecurityConfig extends WebSecurityConfigurerAdapter {
?
 @Autowired
 SpittleRepository spittleRepository;
 @Override
 protected void configure(HttpSecurity httpSecurity) throws Exception {
 // 啟用默認的登錄頁
 httpSecurity
 .formLogin()
 .loginPage("/login")
 .defaultSuccessUrl("/spittle/")
 .failureUrl("/spittle/login?error=true")
 .and()
 .logout()
 .logoutSuccessUrl("/login")
 .logoutUrl("/spittle/login?logout=true")
 .and()
 .rememberMe()
 .tokenValiditySeconds(2439800)
 .key("spittrKey")
 .and()
 .authorizeRequests()
 .antMatchers("/spittle/user/*").hasRole("USER")
 .antMatchers("/spittle/admin/*").hasRole("ADMIN")
 .anyRequest()
 .permitAll()
 ;
 httpSecurity.csrf().disable();
 }
?
?
 /**
 *
 * @param auth
 * @throws Exception
 */
 @Override
 protected void configure(AuthenticationManagerBuilder auth) throws AuthenticationException {
 System.out.println("加載Security。。。讀取權(quán)限");
 // 啟用內(nèi)存用戶儲存
 auth.inMemoryAuthentication()
 .passwordEncoder(NoOpPasswordEncoder.getInstance())
 .withUser("user").password("1").roles("USER").and()
 .withUser("admin").password("1").roles("USER","ADMIN");
 }
?
?
?
?
}
<form method="post" action="/login" >
 <table>
 <tr>
 <td>User:</td>
 <td> <input name="username" type="text" value="" /> </td>
 </tr>
 <tr>
 <td>Password:</td>
 <td> <input name="password" type="password" /> </td>
 </tr>
?
 <tr>
 <td><input id="remember_me" name="remember-me" type="checkbox"/></td>
 <td><label for="remember_me" class="inline">Remember me</label></td>
 </tr>
 <input type="hidden"  name="${_csrf.parameterName}"   value="${_csrf.token}"/>
 <tr>
 <td colspan="2">
 <input name="submit" type="submit" value="Login" />
 <input name="reset" type="reset" value="Reset" />
 </td>
 </tr>
 </table>
?
</form>

未完待續(xù)

基礎(chǔ)的認證雖然完成了,但是沒有從數(shù)據(jù)讀取user信息,需要繼續(xù)來驗證

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容