9.3 拦截请求
对每个请求进行细粒度安全性控制的关键在于重载configure(HttpSecurity) 方法。如下代码中的重载configure,为不同的URL路径有选择性的应用安全性
@Override
protected void configure(HttpSecurity http) throws Exception{
http.authorizeRequests()
.antMatchers("/test1").authenticated() //对路径认证
.antMatchers(HttpMethod.POST,"/test2").authenticated() //对路径的POST请求认证
.anyRequest().permitAll(); //方通其他路径请求,不需要认证和任何的权限
}
除了路径选择,还可以通过authenticated() 和 permitAll() 来定义该如何保护路径。authenticated()要求在执行该请求时,必须已经登录了应用。如果用户没有认证的话,Spring Security的Filter将会捕获该请求,并将用户重定向到应用的登录界面。
其他的用来定义如何保护请求的方法
方法 | 能够做什么 |
---|---|
access(String) | 如果给定的SpEL表达式计算结果为true,就允许访问 |
anonymous() | 允许匿名访问 |
authenticated() | 允许认证过的用户访问 |
denyAll() | 无条件拒绝访问 |
fullyAuthenticated() | 如果用户是完整认证的话(不是通过Remember-me功能认证的),就允许访问 |
hasAnyAuthority(String…) | 如果用户具备给定权限的某一个的话,就允许访问 |
hashAnyRole(String…) | 如果用户具备给定角色的某一个的话,就允许访问 |
hashAuthority(String) | 如果用户具备给定权限的话,就允许访问 |
hasIpAddress(String) | 如果请求来自给定IP地址的话,就允许访问 |
hasRole(String) | 如果用户具备给定角色的话,就允许访问 |
not() | 对其他访问方法的结果求反 |
permitAll() | 无条件访问 |
rememberMe() | 如果用户是通过Remember-me功能认证的,就允许访问 |
可以将任意数量的 antMatchers()、regexMatchers()和anyRequest()连接起来,以满足Web应用安全规则的需要,但是!这些规则会按照给定的顺序发挥作用。所以 要将最为具体的请求路径放在前面,而最不具体的路径(如anyRequest())放在最后面。
9.3.1 SpringEL进行安全保护
SpringEL具有强大的多维度保护机制。它能支持很多SpEL表达式
.antMatchers("/test1").access("hasRole('ROLE_USER')") //具有ROLE_USER角色才能访问的url
安全表达式 | 计算结果 |
---|---|
authentication | 用户的认证对象 |
denyAll | 结果始终为false |
hasAnyRole(list of roles) | 如果用户被授予了列表中的指定角色,结果为true |
hashRole(role) | 如果用户被授予了指定的角色,结果为true |
hasIpAddress(IP Address) | 如果请求来自指定IP的话,结果为true |
isAnonymous() | 如果当前用户为匿名用户,结果为true |
isAuthenticated() | 如果当前用户进行了认证,结果为true |
ifFullyAuthenticated() | 如果当前用户进行了完整的认证的话,结果为true |
isRememberMe() | 如果当前用户是通过Remember-me自动认证的,结果为true |
permitAll | 结果始终为true |
principal | 用户的principal对象 |
.antMatchers("/test1")
.access("hashRole('ROLE_USER') and hasIpAddress('192.168.1.1') ") //指定权限且来自指定的IP
9.3.2 强制通道的安全性
借助requiresChannel()方法,能够为各种URL模式声明所要求的通道。
@Override
protected void configure(HttpSecurity http) throws Exception{
http.authorizeRequests()
.antMatchers("/test1").hasRole("USER")//对路径认证
.antMatchers(HttpMethod.POST,"/test2").hasRole("USER") //对路径的POST请求认证
.anyRequest().permitAll() //方通其他路径请求,不需要认证和任何的权限
.and()
.requiresChannel()
.antMatchers("/test1/test3").requiresSecure(); //需要HTTPS
//.antMatchers("/").requiresInecure(); 始终通过HTTP传送
}
此时,只要是对 /test1/test3 的请求,Spring Security都视为需要安全通道并自动将请求重定向到HTTPS上。
9.3.3 防止跨站请求伪造
跨站请求伪造(CSRF):如果一个站点欺骗用户提交请求到其他服务器,即CSRF攻击。
不去处理的方式:httpSecurity.csfr().disable(); //禁用CSRF防护功能
9.4 认证用户
9.4.1 添加自定义的登录页
9.4.2 启用HTTP Basic认证
HTTP Basic认证(HTTP Basic Authentication)会直接通过HTTP请求本身,对访问应用程序的用户进行认证。在REST客户端向它使用的服务进行认证的场景中,这种方式比较适合。
@Override
protected void configure(HttpSecurity http) throws Exception{
http
.csrf().disable()
.formLogin() //启用默认的登录页
.and()
.httpBasic().realmName("Spittr") //启用HTTP Basic认证
....
}
9.4.3 启用Remember-me
只要登陆过一次,应用程序就会记住你,当再次回到应用的时候就不需要登录了,启用方式:在configure()方法传入的HttpSecurity对象上调用rememberMe( )即可。
@Override
protected void configure(HttpSecurity http) throws Exception{
http
.csrf().disable()
.formLogin() //启用默认的登录页
.and()
.httpBasic().realmName("Spittr") //启用HTTP Basic认证
.and()
.rememberMe()
.tokenValiditySeconds(2419200)
.key("spitterKey")
....
}
默认情况下,这个功能是通过在cookie中存储一个token完成的,这个token最多两周内有效。但是,在这里,我们制定token最多四周内有效(2419200秒)。存储在cookie中的token包含用户名、密码、过期时间和一个私钥—在写入cookie前都进行了MD5哈希。默认情况下,私钥的名为SpringSecured,可以自己命名使它用于Spittr应用。
9.4.4 退出
退出功能是通过Servlet容器中的Filter实现的(默认情况下),这个Filter会拦截针对"/logout"的请求。因此只要发起对"/logout"的请求,就会被Spring Security的LogoutFilter所处理,用户会退出应用,所有的Remember-me token都会被清除掉。在退出完成后,用户浏览器将会重定向到"/login?logout",从而允许用户进行再次登录。
可以通过在configure()配置,重定向指定页面
@Override
protected void configure(HttpSecurity http) throws Exception{
http
.formLogon()
.and()
.logout()
.logoutSuccessUrl("/") //重定向到"/"
.logoutUrl("/signout") //重写默认的LogoutFilter拦截路径
}