原文格式清晰,转载自:https://www.jianshu.com/p/4f9ee6007213
我们知道
spring secuity
是控制URL的访问权限的,那么spring secuity
是怎样拦截匹配URL,我们先看一个接口RequestMatcher
匹配HttpServletRequest
的简单策略接口RequestMatcher
,其下定义了matches
方法,如果返回是true表示提供的请求与提供的匹配规则匹配,如果返回的是false则不匹配。
匹配HttpServletRequest的简单策略接口
RequestMatcher其实现类:
其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将预先定义的ant风格与URL进行比较(一个HttpServletRequest的servletPath + pathInfo)。 查询字符串网址被忽略,匹配是否区分大小写具体取决于传递给构造函数的参数(详细可查看AntPathRequestMatcher的三个入参的构造函数)
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.
使用/** 或 ** 的模式值被视为通用匹配,这将匹配任何请求。 以/** 结尾的模式(并且没有其他通配符)比如 /aaa/** 的模式将匹配/aaa,/aaa/和任何子目录,例如/aaa/bbb/ccc。
所谓Apache Ant的样式路径,有三种通配符的匹配方式
做url匹配的时候,不需要加上context path
,只匹配servletPath
+ pathInfo
,关于context path
,servletPath
,pathInfo
的讲解在第一节已经讲解过了,想了解详细可以去查看。
context path
为/web
secuity-quickstart-url01
secuity-quickstart-url01
org.springframework
spring-webmvc
4.3.13.RELEASE
org.springframework.security
spring-security-web
4.2.3.RELEASE
org.springframework.security
spring-security-config
4.2.3.RELEASE
javax.servlet
javax.servlet-api
3.1.0
provided
javax.servlet.jsp
jsp-api
2.2
provided
secuity-quickstart-url01
org.apache.maven.plugins
maven-war-plugin
3.0.0
false
org.eclipse.jetty
jetty-maven-plugin
9.4.3.v20170317
8001
/web
定义了servletPath
为/v1
public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
//系统启动的时候的根类
@Override
protected Class>[] getRootConfigClasses() {
return new Class>[]{WebAppConfig.class};
}
@Override
protected Class>[] getServletConfigClasses() {
return null;
}
@Override
protected String[] getServletMappings() {
return new String[]{"/v1/*"};
}
}
@EnableWebMvc
@EnableWebSecurity
@ComponentScan("com.zhihao.miao.secuity")
public class WebAppConfig extends WebMvcConfigurerAdapter {
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
}
public class WebAppSecurityInitializer extends AbstractSecurityWebApplicationInitializer {
protected String getDispatcherWebApplicationContextSuffix() {
return AbstractDispatcherServletInitializer.DEFAULT_SERVLET_NAME;
}
}
@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";
}
}
@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请求都不需要权限认证
//http.authorizeRequests().antMatchers(HttpMethod.GET).access("permitAll");
//对/hello 进行匹配,不管HTTP METHOD是什么
//http.authorizeRequests().antMatchers("/v1/hello").hasRole("USER");
//匹配/hello,且http method是POST,需要权限认证
//http.authorizeRequests().antMatchers(HttpMethod.POST, "/v1/world").hasRole("USER");
//匹配 /hello,且http method是GET,不需要权限认证
//http.authorizeRequests().antMatchers(HttpMethod.GET, "/v1/hello").access("permitAll");
//匹配/admin,并且http method不管是什么,需要admin权限
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();
}
}
发现虽然我们配置了context path,但是在权限配置中是不需要配置context path,servletPath需要配置
http://localhost:8001/web/v1/admin
http://localhost:8001/web/v1/hello
secuity-quickstart-url01
其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路径这一点是非常重要的。 这意味着如果您已经将任何servlet映射到以“/String”开头,则还应该指定#setServletPath(String)属性以区分相关映射匹配。
MvcRequestMatcher
是根据server_path
来匹配的。
配置类和上面都差不多,不一样的是下面的类,
public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
//系统启动的时候的根类
@Override
protected Class>[] getRootConfigClasses() {
return new Class>[]{WebAppConfig.class};
}
@Override
protected Class>[] getServletConfigClasses() {
return null;
}
@Override
protected String[] getServletMappings() {
return new String[]{"/v1/*","/v2/*"};
}
}
@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方式,需要权限认证之后才能访问
//http.authorizeRequests().mvcMatchers(HttpMethod.POST,"hello").hasRole("USER");
//我们配置了servletPath,多个servletPath的时候这种配置也能进行url访问的更加细粒度化,
//http://localhost:8001/web/v1/hello不需要权限认证
//http://localhost:8001/web/v2/hello需要权限认证
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();
}
}
servletPath配置的三种模式,
第一种配置方式 servlet Path 为空
第二种配置方式 servlet Path /aaa, /aaa/bbb/
第三种配置方式servlet Path /.do, /.action
这三种情况只有第二种需要去配置servletPath,其他二种都不需要。具体配置的时候自己去调试一下。
MvcRequestMatcher和AntPathRequestMatcher的区别:
如果配置了servlet_path,那么AntPathRequestMatcher在配置antMatchers的时候需要加上servlet_path,比如如果加了
@Override
protected String[] getServletMappings() {
return new String[]{"/v1/*","/v2/*"};
}
那么antMatchers("/v1/hello"),而mvcMatchers不需要加上servlet_path
secuity-quickstart-url02
匹配所有的请求
@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.authorizeRequests().anyRequest().access("permitAll");
//匹配所有的请求都需要USER权限
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都需要权限认证,但是不管是什么权限,不管是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
spring secuity提供了requestMatchers
入口让我们自定义自己的RequestMatcher
实现类
@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");
//参数中type等于1的就不做权限认证,
// 当访问的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
context_path
WebSecurityConfig
的configure(HttpSecurity http)
方法中去定义的,WebSecurityConfig
继承WebSecurityConfigurerAdapter
我们看看WebSecurityConfigurerAdapter
类中的configure
默认的实现是什么:
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 从入门到进阶系列教程