URL匹配RequestMatcher接口詳解

我們知道spring secuity是控制URL的訪問權(quán)限的,那么spring secuity是怎樣攔截匹配URL,我們先看一個接口RequestMatcher

匹配HttpServletRequest的簡單策略接口RequestMatcher,其下定義了matches方法,如果返回是true表示提供的請求與提供的匹配規(guī)則匹配,如果返回的是false則不匹配。

匹配HttpServletRequest的簡單策略接口

RequestMatcher其實現(xiàn)類:

  • AntPathRequestMatcher:重點
  • MvcRequestMatcher:重點
  • RegexRequestMatcher: 根據(jù)正則模式進行匹配
  • AnyRequestMatcher

AntPathRequestMatcher

其javadoc描述如下:

Matcher which compares a pre-defined ant-style pattern against the URL (servletPath + pathInfo) of an HttpServletRequest. The query string of the URL is ignored and matching is case-insensitive or case-sensitive depending on the arguments passed into the constructor.
Matcher將預(yù)先定義的ant風格與URL進行比較(一個HttpServletRequest的servletPath + pathInfo)。 查詢字符串網(wǎng)址被忽略,匹配是否區(qū)分大小寫具體取決于傳遞給構(gòu)造函數(shù)的參數(shù)(詳細可查看AntPathRequestMatcher的三個入?yún)⒌臉?gòu)造函數(shù))

Using a pattern value of /** or ** is treated as a universal match, which will match any request. Patterns which end with /** (and have no other wildcards) are optimized by using a substring match — a pattern of /aaa/** will match /aaa, /aaa/ and any sub-directories, such as /aaa/bbb/ccc.
使用/** 或 ** 的模式值被視為通用匹配,這將匹配任何請求。 以/** 結(jié)尾的模式(并且沒有其他通配符)比如 /aaa/** 的模式將匹配/aaa,/aaa/和任何子目錄,例如/aaa/bbb/ccc。

所謂Apache Ant的樣式路徑,有三種通配符的匹配方式

  1. ? 匹配任意單個字符
    • 匹配0或者任意數(shù)量的字符
  2. ** 匹配0或者更多的目錄

做url匹配的時候,不需要加上context path,只匹配servletPath + pathInfo,關(guān)于context path,servletPath,pathInfo的講解在第一節(jié)已經(jīng)講解過了,想了解詳細可以去查看。

示列

  • 定義依賴
    定義相關(guān)的依賴及jetty插件,設(shè)置了context path/web
<name>secuity-quickstart-url01</name>
    <artifactId>secuity-quickstart-url01</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>4.3.13.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-web</artifactId>
            <version>4.2.3.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-config</artifactId>
            <version>4.2.3.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>jsp-api</artifactId>
            <version>2.2</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>


    <build>
        <finalName>secuity-quickstart-url01</finalName>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>3.0.0</version>
                <configuration>
                    <failOnMissingWebXml>false</failOnMissingWebXml>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.eclipse.jetty</groupId>
                <artifactId>jetty-maven-plugin</artifactId>
                <version>9.4.3.v20170317</version>
                <configuration>
                    <httpConnector>
                        <port>8001</port>
                    </httpConnector>
                    <webApp>
                        <contextPath>/web</contextPath>
                    </webApp>
                </configuration>
            </plugin>
        </plugins>
    </build>
  • 定義系統(tǒng)啟動類:

定義了servletPath/v1

public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

    //系統(tǒng)啟動的時候的根類
    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class<?>[]{WebAppConfig.class};
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        return null;
    }

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

}
  • web入口類
@EnableWebMvc
@EnableWebSecurity
@ComponentScan("com.zhihao.miao.secuity")
public class WebAppConfig extends WebMvcConfigurerAdapter {

    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }
}
  • spring security配置類
public class WebAppSecurityInitializer extends AbstractSecurityWebApplicationInitializer {

    protected String getDispatcherWebApplicationContextSuffix() {
        return AbstractDispatcherServletInitializer.DEFAULT_SERVLET_NAME;
    }
}
  • 具體的Controller
@RestController
public class HelloController {

    @GetMapping("/hello")
    public String hello(){
        return "hello spring secuity";
    }

    @PostMapping("/world")
    public String world(){
        return "world spring secuity";
    }

    @GetMapping("/home")
    public String home(){
        return "home spring secuity";
    }

    @GetMapping("/admin")
    public String admin(){
        return "admin spring secuity";
    }
}
  • 權(quán)限用戶名密碼的具體配置
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication().withUser("zhangsan").password("123456").roles("GUEST");
        auth.inMemoryAuthentication().withUser("zhihao.miao").password("123456").roles("USER");
        auth.inMemoryAuthentication().withUser("lisi").password("12345678").roles("USER", "ADMIN");
    }

    protected void configure(HttpSecurity http) throws Exception {

        //post請求默認的都開啟了csrf的模式,所有post請求都必須帶有token之類的驗證信息才可以進入登陸頁面,這邊是禁用csrf模式
        http.csrf().disable();

        //表示所有的get請求都不需要權(quán)限認證
        //http.authorizeRequests().antMatchers(HttpMethod.GET).access("permitAll");

        //對/hello 進行匹配,不管HTTP METHOD是什么
        //http.authorizeRequests().antMatchers("/v1/hello").hasRole("USER");

        //匹配/hello,且http method是POST,需要權(quán)限認證
        //http.authorizeRequests().antMatchers(HttpMethod.POST, "/v1/world").hasRole("USER");

        //匹配 /hello,且http method是GET,不需要權(quán)限認證
        //http.authorizeRequests().antMatchers(HttpMethod.GET, "/v1/hello").access("permitAll");

        //匹配/admin,并且http method不管是什么,需要admin權(quán)限
        http.authorizeRequests().antMatchers("/v1/admin").hasRole("ADMIN");


        http.authorizeRequests().antMatchers("/**/*.html").access("permitAll");
        http.authorizeRequests().antMatchers("/**/*.css").access("permitAll");
        http.authorizeRequests().antMatchers("/**/*.js").access("permitAll");
        http.authorizeRequests().antMatchers("/**/*.png").access("permitAll");

        http.authorizeRequests().anyRequest().authenticated();

        http.formLogin();
    }
}
  • 驗證

發(fā)現(xiàn)雖然我們配置了context path,但是在權(quán)限配置中是不需要配置context path,servletPath需要配置

http://localhost:8001/web/v1/admin
http://localhost:8001/web/v1/hello

參考資料

secuity-quickstart-url01

MvcRequestMatcher

其javadoc描述如下:

A RequestMatcher that uses Spring MVC's HandlerMappingIntrospector to match the path and extract variables.
RequestMatcher使用Spring MVC的HandlerMappingIntrospector來匹配路徑并提取變量。

It is important to understand that Spring MVC's matching is relative to the servlet path. This means if you have mapped any servlet to a path that starts with "/" and is greater than one, you should also specify the #setServletPath(String) attribute to differentiate mappings.
對我們而言了解Spring MVC的匹配是相對于servlet路徑這一點是非常重要的。 這意味著如果您已經(jīng)將任何servlet映射到以“/String”開頭,則還應(yīng)該指定#setServletPath(String)屬性以區(qū)分相關(guān)映射匹配。

MvcRequestMatcher是根據(jù)server_path來匹配的。

示列

配置類和上面都差不多,不一樣的是下面的類,

  • 定義系統(tǒng)啟動類:
public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

    //系統(tǒng)啟動的時候的根類
    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class<?>[]{WebAppConfig.class};
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        return null;
    }

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

}
  • 權(quán)限用戶名密碼的具體配置
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication().withUser("zhangsan").password("123456").roles("GUEST");
        auth.inMemoryAuthentication().withUser("zhihao.miao").password("123456").roles("USER");
        auth.inMemoryAuthentication().withUser("lisi").password("12345678").roles("USER", "ADMIN");
    }

    protected void configure(HttpSecurity http) throws Exception {

        http.csrf().disable();

        //http://localhost:8001/web/v1/hello get方式 可以訪問
        //http.authorizeRequests().mvcMatchers(HttpMethod.GET,"hello").access("permitAll");
        //http://localhost:8001/web/v1/hello post方式,需要權(quán)限認證之后才能訪問
        //http.authorizeRequests().mvcMatchers(HttpMethod.POST,"hello").hasRole("USER");

        //我們配置了servletPath,多個servletPath的時候這種配置也能進行url訪問的更加細粒度化,
        //http://localhost:8001/web/v1/hello不需要權(quán)限認證
        //http://localhost:8001/web/v2/hello需要權(quán)限認證
        http.authorizeRequests().mvcMatchers(HttpMethod.GET,"hello").servletPath("/v1").access("permitAll");
        http.authorizeRequests().mvcMatchers(HttpMethod.GET,"hello").servletPath("/v2").access("hasRole('USER')");

        http.authorizeRequests().antMatchers("/**/*.html").access("permitAll");
        http.authorizeRequests().antMatchers("/**/*.css").access("permitAll");
        http.authorizeRequests().antMatchers("/**/*.js").access("permitAll");
        http.authorizeRequests().antMatchers("/**/*.png").access("permitAll");

        http.authorizeRequests().anyRequest().authenticated();

        http.formLogin();
    }
}

總結(jié)

servletPath配置的三種模式,
第一種配置方式 servlet Path 為空
第二種配置方式 servlet Path /aaa, /aaa/bbb/
第三種配置方式servlet Path /.do, /.action

這三種情況只有第二種需要去配置servletPath,其他二種都不需要。具體配置的時候自己去調(diào)試一下。

MvcRequestMatcher和AntPathRequestMatcher的區(qū)別:
如果配置了servlet_path,那么AntPathRequestMatcher在配置antMatchers的時候需要加上servlet_path,比如如果加了

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

那么antMatchers("/v1/hello"),而mvcMatchers不需要加上servlet_path

參考資料

secuity-quickstart-url02

AnyRequestMatcher

匹配所有的請求

示列

  • 權(quán)限用戶名密碼的具體配置
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication().withUser("zhangsan").password("123456").roles("GUEST");
        auth.inMemoryAuthentication().withUser("zhihao.miao").password("123456").roles("USER");
        auth.inMemoryAuthentication().withUser("lisi").password("12345678").roles("USER", "ADMIN");
    }

    protected void configure(HttpSecurity http) throws Exception {

        http.csrf().disable();

        //匹配所有請求都不需要權(quán)限控制
        //http.authorizeRequests().anyRequest().access("permitAll");

        //匹配所有的請求都需要USER權(quán)限
        http.authorizeRequests().anyRequest().hasRole("USER");


        http.authorizeRequests().antMatchers("/**/*.html").access("permitAll");
        http.authorizeRequests().antMatchers("/**/*.css").access("permitAll");
        http.authorizeRequests().antMatchers("/**/*.js").access("permitAll");
        http.authorizeRequests().antMatchers("/**/*.png").access("permitAll");

        //我們平時寫的authenticated,指的是上面配置沒有匹配到的url都需要權(quán)限認證,但是不管是什么權(quán)限,不管是USER,GUEST,ADMIN都可以
        http.authorizeRequests().anyRequest().authenticated();

        http.formLogin();
    }
}
  • 驗證
http://localhost:8001/web/v1/hello
http://localhost:8001/web/v1/home
http://localhost:8001/web/v1/admin

參考資料

secuity-quickstart-url03

自定義RequestMatcher

示列

  • 權(quán)限用戶名密碼的具體配置

spring secuity提供了requestMatchers入口讓我們自定義自己的RequestMatcher實現(xiàn)類

@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication().withUser("zhangsan").password("123456").roles("GUEST");
        auth.inMemoryAuthentication().withUser("zhihao.miao").password("123456").roles("USER");
        auth.inMemoryAuthentication().withUser("lisi").password("12345678").roles("USER", "ADMIN");
    }

    protected void configure(HttpSecurity http) throws Exception {

        http.csrf().disable();

        //spring secuity提供了requestMatchers接口,等價于http.authorizeRequests().anyRequest().access("permitAll");
        //http.authorizeRequests().requestMatchers(AnyRequestMatcher.INSTANCE).access("permitAll");

        //參數(shù)中type等于1的就不做權(quán)限認證,
        // 當訪問的url地址為http://localhost:8001/web/v1/hello?type=1,因為type值是1,所以匹配
        http.authorizeRequests().requestMatchers((RequestMatcher) request -> "1".equals(request.getParameter("type"))).access("permitAll");

        http.authorizeRequests().antMatchers("/**/*.html").access("permitAll");
        http.authorizeRequests().antMatchers("/**/*.css").access("permitAll");
        http.authorizeRequests().antMatchers("/**/*.js").access("permitAll");
        http.authorizeRequests().antMatchers("/**/*.png").access("permitAll");

        http.authorizeRequests().anyRequest().authenticated();

        http.formLogin();
    }
}

參考資料

secuity-quickstart-url03

總結(jié)

  1. 做url匹配的時候,不需要加上context_path
  2. 順序很重要,適合的url先匹配到的先應(yīng)用上了。
  3. 我們在做權(quán)限控制的時候都在我們自己定義的WebSecurityConfigconfigure(HttpSecurity http)方法中去定義的,WebSecurityConfig繼承WebSecurityConfigurerAdapter

我們看看WebSecurityConfigurerAdapter類中的configure默認的實現(xiàn)是什么:

    protected void configure(HttpSecurity http) throws Exception {
        logger.debug("Using default configure(HttpSecurity). If subclassed this will potentially override subclass configure(HttpSecurity).");

        http
            .authorizeRequests()
                .anyRequest().authenticated()
                .and()
            .formLogin().and()
            .httpBasic();
    }

我們看到了任何請求需要認證以表單的形式,以httpbasic的方式進行認證。

參考資料

官方文檔
Spring Security 從入門到進階系列教程

?著作權(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)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,618評論 19 139
  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 47,276評論 6 342
  • 從三月份找實習(xí)到現(xiàn)在,面了一些公司,掛了不少,但最終還是拿到小米、百度、阿里、京東、新浪、CVTE、樂視家的研發(fā)崗...
    時芥藍閱讀 42,818評論 11 349
  • 最近因為“薩德”問題,樂天瑪特在中國遭遇到了前所未有的抵制,全中國人都知道抵制樂天瑪特了。 我第一次聽說并見到樂天...
    酒言醉語閱讀 743評論 18 20
  • 初步接觸可變參數(shù)枚舉結(jié)構(gòu)體錯誤處理泛型 由于項目需要和蘋果發(fā)展的趨勢,swift的學(xué)習(xí)勢在必得,swift以其優(yōu)秀...
    itclimb閱讀 360評論 0 0

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