Spring Security 4

作者

yoyoyosiyu@126.com

時(shí)間

2017年9月

簡述

這段時(shí)間一直在不斷的嘗試配置和使用Spring Security, 取得了一些成果盒粮,也大概的理解了一些其中的原理瓷产,擔(dān)心日后忘記气筋,特此記錄一下,作為備忘浦妄。
Spring 的配置分為XML和Java兩種七咧,還有一種是混合贷笛。我用的是混合踏施。至于組件管理石蔗,我用的是Gradle。Maven其實(shí)也差不多畅形。

環(huán)境

  • Intellij Idea
  • gradle
  • java 1.8
  • tomcat 9

首先是一些依賴的組件
build.gradle

group 'com.kgl1688'
version '1.0-SNAPSHOT'

apply plugin: 'java'
apply plugin: 'war'

sourceCompatibility = 1.8

repositories {
    mavenCentral()
}

dependencies {
    compile 'org.springframework:spring-webmvc:4.2.3.RELEASE'
    compile 'org.springframework.security:spring-security-web:4.2.3.RELEASE'
    compile 'org.springframework.security:spring-security-config:4.2.3.RELEASE'

    providedCompile group: 'javax.servlet', name: 'javax.servlet-api', version: '4.0.0'

    testCompile group: 'junit', name: 'junit', version: '4.11'
    testCompile group: 'junit', name: 'junit', version: '4.12'
}

先配置Spring MVC

Spring MVC 的Java配置

src/com/kgl1688/config/WebApplicationInitializer.java

package com.kgl1688.config;

import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

public class WebApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class[] {RootConfigu.class};
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class[] {WebConfig.class};
    }

    @Override
    protected String[] getServletMappings() {
        return new String[] {"/*"};
    }
}

src/com/kgl1688/config/WebConfig.java

package com.kgl1688.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.InternalResourceViewResolver;

@Configuration
@EnableWebMvc
@ComponentScan("com.kgl1688.mvc")
public class WebConfig extends WebMvcConfigurerAdapter {

    @Bean
    public ViewResolver viewResolver() {
        InternalResourceViewResolver internalResourceViewResolver =
                new InternalResourceViewResolver();

        internalResourceViewResolver.setPrefix("/WEB-INF/views/");
        internalResourceViewResolver.setSuffix(".jsp");

        return internalResourceViewResolver;
    }
}

src/com/kgl1688/config/RootConfig.java

package com.kgl1688.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;

@Configuration
@ComponentScan
public class RootConfig {
}

有了這三個(gè)類养距,一個(gè)Spring MVC的環(huán)境就配置完成了。其中幾個(gè)要點(diǎn)如下

  • WebApplicationInitializer
    這個(gè)是Spring MVC 環(huán)境的啟動點(diǎn)日熬,之所以他是啟動點(diǎn)棍厌,因?yàn)樗^承自AbstractAnnotationConfigDispatcherServletInitializer。更細(xì)節(jié)的原因可以度娘竖席。

  • @EnableWebMvc
    這個(gè)注解是啟動Spring MVC 的關(guān)鍵

  • @ComponentScan
    這個(gè)注解會啟動組件掃描耘纱,指示Spring 掃描使用了該注解的類所在的包及其下的所有子包的組件(RootConfiguration 在 com.kgl1688.config 包,那么com.kgl1688.config 以及其下的包都會被掃描)

  • @ImportResource
    這個(gè)注解可以引入XML類型的Beans的定義怕敬,這也是XML和Java混合配置的連接點(diǎn)

Spring Security 的Java配置

添加一個(gè)文件在src/com/kgl1688/config/ 目錄下:
src/com/kgl1688/config/SecurityWebApplicationInitializer.java

package com.kgl1688.config;

import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;

public class SecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer  {
}

不需要實(shí)現(xiàn)和重載任何方法揣炕,有就可以了帘皿,并且在什么包下都沒有關(guān)系东跪。其同樣會被Servlet 3.0 的容器探測到。運(yùn)行測試鹰溜,在后臺的LOG里會顯示如下的信息:

Paste_Image.png

這時(shí)候的Spring Security 環(huán)境的初始化已經(jīng)被引入虽填,但還缺少需要的關(guān)鍵組件。這時(shí)候有兩種選擇:Java 配置 或者 XML配置文件

JAVA類配置

添加 SecurityConfig.java 到 src/com.kgl1688.config目錄下

package com.kgl1688.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.config.annotation.ObjectPostProcessor;
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.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                .withUser("user").password("password").roles("USER");
    }
}

關(guān)鍵點(diǎn):

  • 必須在com.kgl1688.config 包下曹动,因?yàn)橹拔覀兣渲肧pring MVC 環(huán)境的時(shí)候@ComponentScan指示在這個(gè)包里自動掃描組件
  • @EnableWebSecurity斋日, 和 Spring MVC 一樣,這個(gè)注解會啟用Spring Security一些關(guān)鍵的組件墓陈。

自此恶守,Spring Security的環(huán)境就配置好了第献。

XML 配置

首先在RootConfig.java文件的類定義中增加

@ImportResource("classpath:security.xml")

package com.kgl1688.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;

@Configuration
@ImportResource("classpath:security.xml")
@ComponentScan
public class RootConfig {
}

然后添加security.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<b:beans xmlns="http://www.springframework.org/schema/security"
         xmlns:b="http://www.springframework.org/schema/beans"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                        http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd">

    <http />

    <user-service>
        <user name="user" password="password" authorities="ROLE_USER" />
    </user-service>

</b:beans>

這樣就有了最小的Spring Security環(huán)境和一些默認(rèn)的配置。

在默認(rèn)的情況下兔港,訪問除了/login, /logout 這些地址之外都需要認(rèn)證庸毫,在沒有認(rèn)證通過之前都會跳到登錄頁面: /login。因?yàn)槲覀兌x了一個(gè)用戶user, 密碼為password衫樊,我們可以用這個(gè)賬戶登錄飒赃。我們可以通過訪問/logout來退出登錄。這些都是Spring Security 給我們提供的科侈,包括登錄頁面载佳。

修改默認(rèn)的規(guī)則

如果我們只是希望那些我們希望保護(hù)的請求地址要求驗(yàn)證,其他的可以自由訪問臀栈。那么我們需要修改默認(rèn)的規(guī)則來達(dá)到我們的要求
現(xiàn)在我們希望 /secure下的所有請求地址都要求認(rèn)證蔫慧,那么我們可以配置如下:

  • JAVA配置

更改src/com.kgl1688.config/SecurityConfig

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //super.configure(http);
        http
                .authorizeRequests()
                    .antMatchers("/secure/**")
                    .authenticated()
                    .and()
                .formLogin()
                    .and()
                .httpBasic();
    }

  • 等效的XML 配置
<?xml version="1.0" encoding="UTF-8"?>
<b:beans xmlns="http://www.springframework.org/schema/security"
         xmlns:b="http://www.springframework.org/schema/beans"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                        http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd">

    <http>
        <intercept-url pattern="/secure/**" access="authenticated" />
        <form-login />
        <http-basic />
    </http>

    <user-service>
        <user name="user" password="password" authorities="ROLE_USER" />
    </user-service>

</b:beans>

現(xiàn)在訪問 /secure 之下的地址都需要認(rèn)證,認(rèn)證方法支持 FormLogin(表單登錄)和 HttpBasic (API常用的方式权薯,無界面藕漱,通過請求頭攜帶認(rèn)證信息)兩種方式。我們可以通過瀏覽器訪問 /Secure 下的地址崭闲,如果未登錄的話肋联,會跳轉(zhuǎn)到登錄頁面。我們也可以通過 postman這樣的API測試工具測試使用HttpBasic方式刁俭。

為不同的請求地址指定不同的訪問角色

假設(shè)你希望所有的人可以訪問 /secure, 但是只有管理員能訪問 /secure/admin

  • JAVA 配置
    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                .withUser("user").password("password").roles("USER").and()
                .withUser("admin").password("password").roles("ADMIN");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                    .antMatchers("/secure/admin/**")
                    .hasRole("ADMIN")
                    .and()
                .authorizeRequests()
                    .antMatchers("/secure/**")
                    .authenticated()
                    .and()
                .formLogin()
                    .and()
                .httpBasic();
    }
  • XML配置
    <http>
        <intercept-url pattern="/secure/admin/**" access="hasRole('ADMIN')" />
        <intercept-url pattern="/secure/**" access="authenticated" />
        <form-login />
        <http-basic />
        <logout />
    </http>

    <user-service>
        <user name="user" password="password" authorities="ROLE_USER" />
        <user name="admin" password="password" authorities="ROLE_ADMIN" />
    </user-service>

這里唯一需要注意的是 intercept-url定義的順序橄仍。Spring Security的規(guī)則是如果命中 pattern 中的條件,將會應(yīng)用此條規(guī)則牍戚,后續(xù)的其他的 intercept-url都會忽略侮繁。

我們增加了一個(gè)新的用戶 admin,并且讓其具有ADMIN權(quán)限如孝。

為不同的請求提供不同的認(rèn)證方式

如果我們希望不同的請求地址使用不同的認(rèn)證方式宪哩,比如 /api/** 這樣的地址一般是作為api調(diào)用,而不是給瀏覽器頁面顯示給用戶的第晰,我們希望采用HttpBasic認(rèn)證方式锁孟,而其他仍然保留 FormLogin 的方式。
intercept-url 似乎不支持在其中加入 http-basic, intercept-urlhttp-basic是同一級別的茁瘦,都在 http之下品抽。
但是可以有多個(gè)http, 所以我們可以在不同的http下指定不同的認(rèn)證方式。

  • JAVA 配置
package com.kgl1688.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
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.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;

@EnableWebSecurity
public class SecurityConfig {


    @Bean
    public UserDetailsService userDetailsService() throws Exception {
        InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
        manager.createUser(User.withUsername("user").password("password").roles("USER").build());
        manager.createUser(User.withUsername("admin").password("password").roles("ADMIN").build());
        return manager;
    }

    @Configuration
    @Order(1)
    public class ApiWebSecurityConfigurerAdapter extends  WebSecurityConfigurerAdapter {

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http
                    .antMatcher("/api/**")
                    .authorizeRequests()
                        .anyRequest()
                        .authenticated()
                        .and()
                    .httpBasic();
        }
    }

    @Configuration
    public class FormLoginWebSecurityConfigurerAdapter extends  WebSecurityConfigurerAdapter {

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http
                    .authorizeRequests()
                        .antMatchers("/secure/admin/**")
                        .hasRole("ADMIN")
                        .and()
                    .authorizeRequests()
                        .antMatchers("/secure/**")
                        .authenticated()
                        .and()
                    .formLogin()
                        .and()
                    .httpBasic();
        }
    }
}

這里必須要注意的是antMatcher("/api/**")的寫法甜熔,他寫在authorizeRequests()之前圆恤,這和寫在authorizeRequests()之后有不同的含義,寫在前面是設(shè)置http對象的腔稀,而寫在authorizeRequests之后是設(shè)置authorizeRequests調(diào)用后返回的ExpressionInterceptUrlRegistry對象的盆昙。如果觀察下面與之等效的XML配置羽历,它實(shí)際上相當(dāng)與 <http> 的 pattern 屬性。

在XML配置中淡喜,<http>有出現(xiàn)的前后次序窄陡,可是在JAVA代碼中,反射機(jī)制并不會因?yàn)轭愒诖a中寫的位置的前后而有所區(qū)別拆火,所以必須依賴于 @Order注解跳夭。沒有@order注解的相當(dāng)于優(yōu)先級在最后。

另外一個(gè)要補(bǔ)充的知識點(diǎn)是JAVA代碼中配置的寫法規(guī)則们镜。http對象 和 XML配置中的 <http> 類似币叹。而 authorizeRequests 相當(dāng)于 <http>中的 <intercept-url>, formLogin 等價(jià)于 <form-login>,httpBasic 等價(jià)于 <http-basic>。 而 and() 就相當(dāng)于閉合一個(gè)元素模狭,比如 .formLogin().and()就等于<form-login></form-login>颈抚。因此 http.antMatcher("/api/**") 就等價(jià)于 <http pattern="/api/**">

  • XML配置
<?xml version="1.0" encoding="UTF-8"?>
<b:beans xmlns="http://www.springframework.org/schema/security"
         xmlns:b="http://www.springframework.org/schema/beans"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                        http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd">

    <http pattern="/api/**">
        <intercept-url pattern="/api/**" access="authenticated" />
        <http-basic />
    </http>

    <http>
        <intercept-url pattern="/secure/admin/**" access="hasRole('ADMIN')" />
        <intercept-url pattern="/secure/**" access="authenticated" />
        <form-login />
        <http-basic />
        <logout />
    </http>

    <user-service>
        <user name="user" password="password" authorities="ROLE_USER" />
        <user name="admin" password="password" authorities="ROLE_ADMIN" />
    </user-service>

</b:beans>
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市嚼鹉,隨后出現(xiàn)的幾起案子贩汉,更是在濱河造成了極大的恐慌,老刑警劉巖锚赤,帶你破解...
    沈念sama閱讀 211,743評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件匹舞,死亡現(xiàn)場離奇詭異,居然都是意外死亡线脚,警方通過查閱死者的電腦和手機(jī)赐稽,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來浑侥,“玉大人姊舵,你說我怎么就攤上這事≡⒙洌” “怎么了括丁?”我有些...
    開封第一講書人閱讀 157,285評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長伶选。 經(jīng)常有香客問我史飞,道長,這世上最難降的妖魔是什么考蕾? 我笑而不...
    開封第一講書人閱讀 56,485評論 1 283
  • 正文 為了忘掉前任祸憋,我火速辦了婚禮会宪,結(jié)果婚禮上肖卧,老公的妹妹穿的比我還像新娘赋除。我一直安慰自己汹忠,他們只是感情好肄扎,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,581評論 6 386
  • 文/花漫 我一把揭開白布沪羔。 她就那樣靜靜地躺著,像睡著了一般葵姥。 火紅的嫁衣襯著肌膚如雪荷鼠。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,821評論 1 290
  • 那天榔幸,我揣著相機(jī)與錄音允乐,去河邊找鬼。 笑死削咆,一個(gè)胖子當(dāng)著我的面吹牛牍疏,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播拨齐,決...
    沈念sama閱讀 38,960評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼鳞陨,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了瞻惋?” 一聲冷哼從身側(cè)響起厦滤,我...
    開封第一講書人閱讀 37,719評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎歼狼,沒想到半個(gè)月后掏导,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,186評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡羽峰,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,516評論 2 327
  • 正文 我和宋清朗相戀三年碘菜,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片限寞。...
    茶點(diǎn)故事閱讀 38,650評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡忍啸,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出履植,到底是詐尸還是另有隱情计雌,我是刑警寧澤,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布玫霎,位于F島的核電站凿滤,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏庶近。R本人自食惡果不足惜翁脆,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,936評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望鼻种。 院中可真熱鬧反番,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,757評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至枫疆,卻和暖如春爵川,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背息楔。 一陣腳步聲響...
    開封第一講書人閱讀 31,991評論 1 266
  • 我被黑心中介騙來泰國打工寝贡, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人值依。 一個(gè)月前我還...
    沈念sama閱讀 46,370評論 2 360
  • 正文 我出身青樓兔甘,卻偏偏與公主長得像,于是被迫代替她去往敵國和親鳞滨。 傳聞我的和親對象是個(gè)殘疾皇子洞焙,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,527評論 2 349

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

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)拯啦,斷路器澡匪,智...
    卡卡羅2017閱讀 134,633評論 18 139
  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 46,773評論 6 342
  • jHipster - 微服務(wù)搭建 CC_簡書[http://www.reibang.com/u/be0d56c4...
    quanjj閱讀 800評論 0 2
  • 嗨、 老頭褒链、好開心可以這么稱呼你唁情、“老頭”是我情犢初開許諾給自己今生最愛男人的稱呼、這是一封寫給你的信甫匹、也算是...
    Eleven9216閱讀 233評論 0 2
  • 一直單身,不是沒有人追恍箭,只是一直沒有一個(gè)讓我有那種感覺的人刻恭。也有符合我對對象一切外在要求的人追求,只是不知道為什么...
    王爾王爾閱讀 522評論 0 0