Spring-Security-教程(一)---簡單的登錄認證

一只祠、Spring Security 概述

Spring Security是一個功能強大且高度可定制的身份驗證和訪問控制框架兜蠕。它實際上是保護基于spring的應(yīng)用程序的標準熊杨,也是一個專注于向Java應(yīng)用程序提供身份驗證和授權(quán)的框架晶府。
連接:Spring Security

博客地址:https://blog.ffspace.cn
項目代碼:https://github.com/Bootcap/spring-security-study-session

二钻趋、環(huán)境要求

  • JDK ≥1.7
  • Maven 3.0+
  • IntelliJ IDEA/eclipse

【提示】: 本文以 IDEA 和 maven 以及Spring Boot 2.0 為例進行教程

三蛮位、使用Maven進行構(gòu)建

在pom.xml文件中引入相關(guān)的jar包(不含security)

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="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 http://maven.apache.org/maven-v4_0_0.xsd">

  <modelVersion>4.0.0</modelVersion>

  <name>spring-security-study-session</name>
  <groupId>com.bootcap.session.security</groupId>
  <artifactId>spring-security-study-session</artifactId>
  <version>1.0.0-SNAPSHOT</version>

  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.0.1.RELEASE</version>
  </parent>

  <properties>
    <java.version>1.8</java.version>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
  </properties>


  <dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework.security</groupId>
      <artifactId>spring-security-test</artifactId>
      <scope>test</scope>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
      </plugin>
    </plugins>
  </build>
</project>

在項目的src/main/resources下進行配置

# 端口號
server:
  port: 8080

spring:
  # thymeleaf配置
  thymeleaf:
    enabled: true
    encoding: UTF-8
    mode: HTML
    servlet:
      content-type: text/html
    prefix: classpath:/templates/
    suffix: .html

四购对、創(chuàng)建一個不受保護的web頁面

Web頁面包含兩個簡單的視圖:index主頁和“hello”頁面陶因,都定義在Thymeleaf模板中楷扬。

4.1 Index頁面

路徑:src/main/resources/templates/index.html

<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Spring Security Index</title>
</head>
<body>
    <h1>Index Page</h1>
    <a th:href="@{/hello}">點擊前往hello頁面</a>
</body>
</html>

【注意】: 使用thymeleaf時需要在<html>標簽后面加入xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" 屬性,才能被編譯器解析為thymeleaf模板

4.2 Hello頁面

路徑:src/main/resources/templates/hello.html

<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Hello</title>
</head>
<body>
    <h1>Hello Spring Security</h1>
</body>
</html>

從上面index.html可以看到包含了一個"/hello"連接點擊跳轉(zhuǎn)到hello.html頁面的簡單流程

五躲株、配置Spring MVC視圖控制器

由于Web應(yīng)用程序基于Spring MVC霜定。 因此廊鸥,需要配置視圖控制器來暴露這些模板惰说。

路徑:src/main/java/com/bootcap/session/security/configuration/TemplateConfig.java

package com.bootcap.session.security.configuration;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;


@Configuration
public class TemplateConfig implements WebMvcConfigurer {

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/").setViewName("index");
        registry.addViewController("/index").setViewName("index");
        registry.addViewController("/hello").setViewName("hello");
        registry.addViewController("/login").setViewName("login");
    }
}

六、配置Spring Boot啟動類

6.1 到了這一步典挑,我們需要將項目運行查看是否無異常您觉,才進行下一步操作授滓,因此需要配置spring boot啟動類

路徑:src/main/java/com/bootcap/session/security/app/Application.java

package com.bootcap.session.security.app;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;

@SpringBootApplication
@ComponentScan(basePackages = {"com.bootcap.session.security"})
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class,args);
    }
}
6.2 運行main方法褒墨,并在瀏覽器地址欄輸入:http://localhost:8080/ 如果看到index.html頁面,說明已經(jīng)成功運行
image
6.3 點擊跳轉(zhuǎn)到hello頁面鏈接浑玛,無需任何認證即可進行跳轉(zhuǎn)
image

【小竅門】: 為什么要使用@ComponentScan注解:

  • 如果你的其他包都在使用了@SpringBootApplication注解的main方法所在的包及其下級包顾彰,則SpringBoot會自動幫你把其他包都掃描胃碾。
  • 如果你有一些bean所在的包仆百,不在@SpringBootApplication注解main方法的包及其下級包,那么你需要手動加上@ComponentScan注解并指定要尋找的bean所在的包路徑吁讨。(詳見上述代碼中package的不同之處)

七、引入并使用Spring Security

在上述的兩個視圖中排龄,我們希望在訪問"/hello"時需要登錄才能夠進入Hello頁面橄维。此時我們可以通過Spring Security來實現(xiàn)拴曲。(如果Spring Security在類路徑上,則Spring Boot會使用"Basic"認證自動保護所有HTTP請求兑障,也可以自定義設(shè)置)

7.1 pom.xml加入Spring Security

<dependencies>
  ...
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
  ...
</dependencies>

7.2 配置只有認證過的用戶才能訪問hello.html

路徑:src/main/java/com/bootcap/session/security/configuration/WebSecurityConfig.java

package com.bootcap.session.security.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.WebSecurityConfigurerAdapter;

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/","/index").permitAll() // permitAll被允許訪問
                .anyRequest().authenticated() // 其余的請求需要認證后才可允許訪問
                .and()
            .formLogin()
                .loginPage("/login")
                .permitAll()
                .and()
            .logout()
            .permitAll();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication() // 在內(nèi)存中進行身份驗證
                .withUser("user")
                .password("password")
                .roles("USER");
    }

}

【說明】:

  • WebSecurityConfig類使用了@EnableWebSecurity注解,以啟用Spring Security的Web安全支持蕉汪。

  • configure(HttpSecurity)方法自定義有哪些url需要被認證流译,哪些不需要福澡。當用戶登錄后將會被重定向請求到需要身份認證的頁面(hello.html)驹马,否則在用戶未登錄的情況下將會跳轉(zhuǎn)到登錄頁面

  • configure(AuthenticationManagerBuilder)方法用于設(shè)置認證的條件保存于內(nèi)存中,用戶名為“user”算利,密碼為“123456”泳姐,角色為User。同時該方法也可以修改認證方式為jdbc進行認證

【注意】: 使用了Spring Boot 2.0以上的Security會存在There is no PasswordEncoder mapped for the id "null"異常缎患,解決方案見底部“常見問題”

7.3 創(chuàng)建登錄頁面(認證時需要用到)

路徑:src/main/resources/templates/login.html

<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>登錄頁面</title>
</head>
<body>
<div th:if="${param.error}">
    用戶名或密碼不正確
</div>
<div th:if="${param.logout}">
    你已經(jīng)退出登錄
</div>
<form th:action="@{/login}" method="post">
    <div><label> 用戶名: <input type="text" name="username"/> </label></div>
    <div><label> 密&nbsp;&nbsp;&nbsp;碼: <input type="password" name="password"/> </label></div>
    <div><input type="submit" value="登錄"/></div>
</form>
</body>
</html>

【說明】:

  • 該登錄頁面會將用戶名和密碼以表單形式提交到"/login"挤渔。

  • 在Spring Security提供了一個攔截請求并驗證的過濾器判导,在用戶未通過認證的情況下會重定向到"/login?error",并且顯示相應(yīng)的錯誤信息骡楼。注銷成功后鸟整,Spring Security會將地址重定向到"/login?logout"朦蕴,我們即可在頁面中看到相應(yīng)的登出信息

7.4 修改hello.html

在認證成功后跳轉(zhuǎn)到hello.html頁面吩抓,我們希望能夠看到登錄的用戶名,同時允許用戶退出登錄,因此我們需要修改hello.html頁面

路徑:src/main/resources/templates/hello.html

<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Hello</title>
</head>
<body>
    <h1 th:inline="text">Hello [[${#httpServletRequest.remoteUser}]]</h1>
    <form th:action="@{/logout}" method="post">
        <input type="submit" value="退出登錄" />
    </form>
</body>
</html>

【說明】:

  • 我們在hello.html頁面中使用了HttpServletRequest#getRemoteUser()的thymeleaf集成來顯示用戶名伴栓。

  • 頁面中退出登錄表單會將請求提交到"/logout"钳垮,成功注銷后程序會重定向到"/login?logout"饺窿。

7.5 啟動應(yīng)用程序

通過文章剛開始的 【配置Spring Boot啟動類】模塊啟動應(yīng)用程序移斩,這里不再過多介紹。

八肠套、測試

8.1 應(yīng)用啟動后, 在瀏覽器中訪問 http://localhost:8080. 就能訪問到Index頁面
image
8.2 點擊跳轉(zhuǎn)到hello.html頁面你稚,會發(fā)現(xiàn)沒有認證不允許進入入宦,程序自動跳轉(zhuǎn)到登錄頁
image
8.3 我們輸入錯誤的信息會提示用戶名或密碼不正確乾闰,同時地址欄的地址也變成了"/login?error"
image
8.4 下面我們輸入正確的賬號登錄(用戶名:user涯肩,密碼:123456)

我們會發(fā)現(xiàn)在控制臺出現(xiàn)了異常(解決方案:詳見底部【常見問題】)


image
8.5 異常解決后我們再進行測試已經(jīng)可以正常地跳轉(zhuǎn)到hello.html頁面,并顯示了登錄的用戶名
image
8.6 最后我們點擊退出登錄疗垛,即可完成注銷操作(此時地址欄"/login?logout"贷腕,并且跳回登錄頁面提示"你已經(jīng)退出登錄")
image

九泽裳、總結(jié)

恭喜破婆!你已經(jīng)開發(fā)了一個簡單的Spring Security程序。

常見問題

【問題一】:

在測試中瀑梗,我們正常登錄時候出現(xiàn)了異常:java.lang.IllegalArgumentException: There is no PasswordEncoder mapped for the id "null"

【解答】:通過問題我們知道該異常來自認證信息WebSecurityConfig的方法configure(AuthenticationManagerBuilder)抛丽。所以我們查閱官方文檔嚎朽,發(fā)現(xiàn)在spring boot 2.0之上使用的是Spring Security 5.0版本,同時也指出使用密碼校驗時候需要進行密碼加密狡门。

因此其馏,改動如下:

方法1:在Application.java啟動類下加入該方法爆安,關(guān)閉密碼加密校驗.(不推薦)
 @Bean
    public static PasswordEncoder passwordEncoder() {
        return NoOpPasswordEncoder.getInstance();
    }
    
方法2:在WebSecurityConfig.java類的configure(AuthenticationManagerBuilder)進行如下改動
 @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//      auth.inMemoryAuthentication() // 在內(nèi)存中進行身份驗證
//              .withUser("user")
//              .password("password")
//              .roles("USER");

        auth.inMemoryAuthentication() // 在內(nèi)存中進行身份驗證
                .passwordEncoder(new BCryptPasswordEncoder())
                .withUser("user")
                .password(new BCryptPasswordEncoder().encode("123456"))
                .roles("USER");
                
//        auth.userDetailsService(userService).passwordEncoder(new BCryptPasswordEncoder());
    }

小技巧

在我們開發(fā)的過程中扔仓,我們經(jīng)常會遇到修改一點點東西時候,都需要重啟操作來生效撬码。因此呜笑,spring boot為我們提供了一個devTool熱更新工具,無需重啟即可生效

pom.xml中引入相關(guān)jar
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.0.1.RELEASE</version>
</parent>
<properties>
    <java.version>1.8</java.version>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
</properties>
<dependencies>
 ... 
  <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-devtools</artifactId>
  </dependency>
...
<dependencies>
如何使用

【提示】: 由于我自己使用的是idea進行開發(fā),因此對eclipse的引入是否需要快捷鍵并不是很清楚凰慈,提供了相關(guān)鏈接

IDEA使用:

  • 修改了java類的地方驼鹅,使用Ctrl+Shift+F9進行熱更新
  • 靜態(tài)頁面/模板頁面,使用Ctrl+F9進行熱更新
  • 快捷鍵使用后不生效堰酿?前往File-Settings-Compiler-Build Project automatically選項開始idea自動編譯

eclipse使用:

附錄:HttpSecurity類的常用方法

方法 說明
openidLogin() 基于OpenId驗證
headers() 將安全頭添加到響應(yīng)
cors() 跨域配置
sessionManagement() session會話管理
portMapper() 配置一個PortMapper(HttpSecurity#(getSharedObject(class)))坎藐,供SecurityConfigurer對象使用 PortMapper 從 HTTP 重定向到 HTTPS 或者從 HTTPS 重定向到 HTTP。默認情況下岩馍,Spring Security使用一個PortMapperImpl映射 HTTP 端口8080到 HTTPS 端口8443蛀恩,HTTP 端口80到 HTTPS 端口443
jee() 配置容器預(yù)認證,默認為Servlet容器進行管理
x509() 配置x509認證
rememberMe() 配置“記住我”的驗證
authorizeRequests() 允許HttpServletRequest限制訪問
requestCache() 允許配置請求緩存
exceptionHandling() 允許配置錯誤處理
logout() 退出登錄壳咕。訪問URL”/ logout”谓厘,使HTTP Session無效來清除用戶,清除已配置的任何#rememberMe()身份驗證竟稳,清除SecurityContextHolder熊痴,然后重定向到”/login?logout”
anonymous() 允許配置匿名用戶訪問果善。默認情況下,匿名用戶將使用org.springframework.security.authentication.AnonymousAuthenticationToken表示盏混,并包含角色 “ROLE_ANONYMOUS”
formLogin() 指定用于表單身份驗證。
oauth2Login() 用于OAuth 2.0 或OpenID的身份驗證
requiresChannel() 配置通道安全许赃。
httpBasic() 配置Http Basic驗證
addFilterAt() 在指定的Filter類位置添加過濾器

下一篇:Spring Security 入門教程(二) - 基于數(shù)據(jù)庫信息進行驗證

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末弹谁,一起剝皮案震驚了整個濱河市句喜,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌植康,老刑警劉巖展懈,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件存崖,死亡現(xiàn)場離奇詭異,居然都是意外死亡冗栗,警方通過查閱死者的電腦和手機供搀,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來军浆,“玉大人乒融,你說我怎么就攤上這事摄悯。” “怎么了申钩?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵撒遣,是天一觀的道長。 經(jīng)常有香客問我义黎,道長,這世上最難降的妖魔是什么泻云? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任宠纯,我火速辦了婚禮层释,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘勃救。我一直安慰自己治力,他們只是感情好宵统,可當我...
    茶點故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布马澈。 她就那樣靜靜地躺著弄息,像睡著了一般。 火紅的嫁衣襯著肌膚如雪涤伐。 梳的紋絲不亂的頭發(fā)上缨称,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天睦尽,我揣著相機與錄音,去河邊找鬼山害。 笑死,一個胖子當著我的面吹牛浪慌,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播匙赞,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼涌庭,長吁一口氣:“原來是場噩夢啊……” “哼欧宜!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起席镀,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤豪诲,失蹤者是張志新(化名)和其女友劉穎挂绰,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體交播,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡秦士,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年隧土,在試婚紗的時候發(fā)現(xiàn)自己被綠了乏梁。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡卖毁,死狀恐怖亥啦,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情奴拦,我是刑警寧澤届吁,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布疚沐,位于F島的核電站,受9級特大地震影響痴施,放射性物質(zhì)發(fā)生泄漏究流。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一神得、第九天 我趴在偏房一處隱蔽的房頂上張望循头。 院中可真熱鬧炎疆,春花似錦形入、人聲如沸缝左。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽耳舅。三九已至,卻和暖如春馏予,著一層夾襖步出監(jiān)牢的瞬間盔性,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工蛹尝, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留悉尾,地道東北人焕襟。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像务漩,于是被迫代替她去往敵國和親它褪。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,592評論 2 353

推薦閱讀更多精彩內(nèi)容