Spring Security系列之核心概念

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring Security系列之核心概念相关的知识,希望对你有一定的参考价值。

参考技术A 提到安全,相信大家的第一反应都是加密,什么MD5、AES、SHA-256算法之类东西。没错加密各种算法是安全的基础。针对于基础的安全设施, Java Security 模块 提供了各种安全域的API、PKI、密码学及其内建实现、安全通信、认证、访问控制等丰富的功能,这些都是安全的基础。在Web应用的开发中,对于各种攻击防守,对于认证、授权的实现实现方案是极为重要的。Spring Security项目就是认证、授权、防攻击实现方案的集成框架。学习框架之前,必须要对安全领域的核心的概念进行梳理,这也是 Spring Security 本身所关注的点。

对请求资源的用户身份进行验证的过程,解决的是“这是谁请求的?”的问题。

确认当前用户是否有权限访问资源的过程,解决的是“他能不能做这个操作?”的问题。

跨站请求伪造(Cross Site Request Forgery)

请求是由外部网站伪造发送到目标服务服务器,而不是由用户主动发送请求至目标服务器这种伪造请求就是叫做CSRF。

同步令牌模式(Synchronizer Token Pattern) ,针对Post请求(避免get,会导致令牌泄漏),在请求参数中(一般是头部)增加同步令牌,服务器校验传入令牌的一致性判断是否是正确请求。请求令牌是从Cookie中获取的,由于同源策略外部站点无法获得令牌。能很好的解决该问题。

通常存在用户Session中。为什么不存在cookies中?这是一种早期处理方案,早期一些系统将token存在cookies中但是会出现了漏洞同 Ruby on Rails的CSRF保护绕过一样 ,同时系统失去了紧急情况下对于令牌的失效保护。web应用开发中关键信息存放在后台是一种比较安全的方式。令牌可能会随着Session过期而过期,可以通过主动取或者被动刷新的方式处理。

SameSite 是Http协议中的一个属性, 参考 。这是一种紧急的方案 ,实现防止CSRF攻击,可以在session cookie中设置如下不同的值:

针对cookie中存储了会话信息的请求如果丢失了cookie就会进入授权登录操作。使用SameSite 属性来防攻击要注意一些用户体验的场景,如Strict模式下邮件发送的地址就会失去了登录态,用户就需要再次登录可能会造成不好的体验。

从应用层面讲一般针对于浏览器用户,非浏览器场景就需要禁止CSRF Protection。对CSRF定义中它是依赖浏览器的,容易和中间人攻击混淆。

特别需要注意的具体场景如 login,logout,multipart是要重点进行CSRF保护。

在HTTP协议中有一些首部用于安全处理,本模块对Spring Security中关键安全首部进行讲解。其他参考: Security HTTP Response Headers , developer.mozilla.org

控制浏览器缓存用户浏览内容的首部。Spring Security 默认禁用缓存,可以避免敏感信息的泄露,如用户登录账号查看敏感信息后退出登录恶意用户通过浏览器回退查看到敏感信息。

针对Content-Type不存在的时候是否采用 内容嗅探(content sniffing) 的方式处理控制的首部。为了优化体验浏览器会自动判断响应内容的类型然后进行渲染。禁用sniff可以避免XSS攻击。因为 恶意用户可以创建一个postscript的js文档并用它来执行XSS攻击 。

Http严格传输安全,HTST(Http Strict Transport Security)。很多用户习惯输入不带https协议的域名,中间人可能会拦截到http并窃取https的响应给用户造成中间人攻击。Strict-Transport-Security首部将指导浏览器将其设置为严格的安全传输地址。 参考RFC6797

Http公钥固定(HPKP),用来告诉Web客户端将特定的加密公钥于某个Web服务器相关联,以降低使用证书伪造的MITM攻击的风险。 已被废弃 。

是否允许自己的网站被嵌入到其他网站。用来避免点击劫持(Click Jacking)

针对检测到跨域脚本攻击的中止操作,现代化的浏览器可以通过 Content-Security-Policy 针对不支持CSP的浏览器该保护的是很有效的。该首部没有被全力支持,只有少部分浏览器是支持的如:IE,Safari。

CSP是作为一种向客户端传递安全策略的机制,每一种安全策略代表了一组安全指令,每个指令对资源的呈现做了限制。 Content-Security-Policy , Content-Security-Policy-Report-Only

Referrer 首部标记的是资源最初来源, Referrer-Policy 主要就是控制多少引用信息包含在请求中。

为开发者提供了选择性禁用、启用、修改固定API或者浏览器功能的机制。

提供了通过首部清除浏览器侧数据的方式,例如登出的时候设置首部来清除浏览器侧的相关数据。

爆破专栏丨Spring Security系列教程之Spring Security的四种权限控制方式

原创:一一哥

前言:

在前面的章节中,一一哥 已经给大家介绍了Spring Security的很多功能,在这些众多功能中,我们知道其核心功能其实就是认证+授权。

在前面我们分别基于内存模型、基于默认的数据库模型、基于自定义数据库模型实现了认证和授权功能,但是不管哪种方式,我们对某个接口的拦截限制,都是通过编写一个SecurityConfig配置类,在该类的configure (Http Security http)方法中,通过http. authorize Requests ( ). antMatchers ("/admin/**")…这样的代码进行的权限控制。

这种权限控制方法虽然也可以实现对某些接口的拦截或放行,但是不够灵活,其实Spring Security对接口的拦截或放行的写法,还有另外的方式,接下来请跟我学习一下吧!

一. 权限控制方式

在Spring Security 中,我们既可以使用 Spring Security 提供的默认方式进行授权,也可以进行自定义授权,总之在Spring Security中权限控制的实现方式是比较灵活多样的。在Spring Security 中,对接口的拦截或放行,有四种常见的权限控制方式:

利用Ant表达式实现权限控制;

利用授权注解结合SpEl表达式实现权限控制;

利用过滤器注解实现权限控制;

利用动态权限实现权限控制。

对上面说到的四种权限控制方式,我们接下来分别进行讲解实现。

二. 利用Ant表达式实现权限控制

利用Ant表达式的权限控制方式,是我们之前一直在使用的权限控制方式,在进行代码实现之前,我先对这种方式的底层实现进行简单分析。

1. Spring Security中的权限控制方法

在Spring Security中,有一个Security Expression Operations 接口,在该接口中定义了一系列的方法,用于用户权限的设置,如下图:

SecurityExpressionOperations接口中的

这些方法作用如下图所示:

2. Spring Security中的权限控制粒度

这个接口有一个SecurityExpressionRoot子类,该类提供了基于表达式的权限控制实现方式。而这个SecurityExpressionRoot 又有两个实现子类,分别用于实现 URL Web接口粒度的权限控制和方法粒度的权限控制,如下图所示:

3. 代码实现

从上面的小节中,我们知道在Spring Security中,支持2种粒度的权限控制,即URL Web接口粒度 和方法粒度,而我们这里所谓的 Ant表达式授权控制方式,就是通过Ant表达式来控制 URL 接口的访问权限。

那么如果我们需要对URL接口粒度进行权限控制,按如下代码即可实现:

@Override

protected void configure(HttpSecurity http) throws Exception {

http.authorizeRequests()

.antMatchers("/admin/**")

.hasRole(“ADMIN”)

.antMatchers("/user/**")

.hasRole(“USER”)

.antMatchers("/visitor/**")

.permitAll()

.anyRequest()

.authenticated()

.and()

.formLogin()

.permitAll()

.and()

//对跨域请求伪造进行防护---->csrf:利用用户带有登录状态的cookie进行攻击的手段

.csrf()

.disable();

}

以上代码中,/admin/ 格式的路径需要 admin 角色才可以访问,/user/ 格式的路径需要 user 角色才可以访问,/visitor/** 格式的路径可以直接访问,其他接口路径则需要登录后才能访问。

三. 利用授权注解结合SpEl表达式实现权限控制

1. 授权注解

除了可以使用上面的Ant表达式进行授权实现,我们也可以在方法上添加授权注解来权限控制,常用的授权注解有3个:

@PreAuthorize:方法执行前进行权限检查;

@PostAuthorize:方法执行后进行权限检查;

@Secured:类似于 @PreAuthorize。

2. 代码实现

要想利用以上3个授权注解进行权限控制,我们首先需要利用@EnableGlobalMethodSecurity注解开启授权注解功能,代码如下:

@Configuration

@EnableGlobalMethodSecurity(prePostEnabled = true,securedEnabled = true)

public class SecurityConfig extends WebSecurityConfigurerAdapter {

}

然后在具体的接口方法上利用授权注解进行权限控制,代码如下:

@RestController

public class UserController {

@Secured({“ROLE_USER”})

//@PreAuthorize(“principal.username.equals(‘user’)”)

@GetMapping("/user/hello")

public String helloUser() {

return “hello, user”;

}

@PreAuthorize(“hasRole(‘ADMIN’)”)

@GetMapping("/admin/hello")

public String helloAdmin() {

return “hello, admin”;

}

@PreAuthorize("#age>100")

@GetMapping("/age")

public String getAge(@RequestParam(“age”) Integer age) {

return String.valueOf(age);

}

@GetMapping("/visitor/hello")

public String helloVisitor() {

return “hello, visitor”;

}

}

可以看出,这种写法明显比利用Ant表达式进行权限控制更灵活方便,所以开发时这种写法很常用。

四. 利用过滤器注解实现权限控制

1. 过滤器注解简介

在Spring Security中还提供了另外的两个注解,即@PreFilter和@PostFilter,这两个注解可以对集合类型的参数或返回值进行过滤。使用@PreFilter和@PostFilter时,Spring Security将移除对应表达式结果为false的元素。

2. @PostFilter的用法

@PostFilter注解主要是用于对集合类型的返回值进行过滤,filterObject是@PostFilter中的一个内置表达式,表示集合中的元素对象。

@Slf4j

@RestController

public class FilterController {

/**

* 只返回结果中id为偶数的user元素。

* filterObject是@PreFilter和@PostFilter中的一个内置表达式,表示集合中的当前对象。

*/

@PostFilter(“filterObject.id%2==0”)

@GetMapping("/users")

public ListgetAllUser() {

Listusers = new ArrayList<>();

for (int i = 0; i < 10; i++) {

users.add(new User(i, “yyg-” + i));

}

return users;

}

}

我们启动浏览器进行测试,可以看到测试接口中只返回了id为偶数的元素。

3. @PreFilter的用法

使用@PreFilter也可以对集合类型的参数进行过滤,当@PreFilter标注的方法内拥有多个集合类型的参数时,可以通过@PreFilter的filterTarget属性来指定当前是针对哪个参数进行过滤的;而filterObject是@PreFilter中的一个内置表达式,表示集合中的元素对象。

为了方便测试,我们在Service层中进行过滤操作,然后在Controller层中进行调用。

FilterService类中的方法定义:

@Slf4j

@Service

public class FilterService {

/**

* 当@PreFilter标注的方法内拥有多个集合类型的参数时,

* 可以通过@PreFilter的filterTarget属性来指定当前是针对哪个参数进行过滤的。

*/

@PreFilter(filterTarget = “ids”, value = “filterObject%2==0”)

public ListdoFilter(Listids, Listusers) {

log.warn(“ids=” + ids.toString());

log.warn(“users=” + users.toString());

return ids;

}

}

在Controller中定义一个测试接口:

@Slf4j

@RestController

public class FilterController {

/**

* 只返回结果中id为偶数的user元素。

* filterObject是@PreFilter和@PostFilter中的一个内置表达式,表示集合中的当前对象。

*/

@PostFilter(“filterObject.id%2==0”)

@GetMapping("/users")

public ListgetAllUser() {

Listusers = new ArrayList<>();

for (int i = 0; i < 10; i++) {

users.add(new User(i, “yyg-” + i));

}

return users;

}

@Autowired

private FilterService filterService;

@GetMapping("/users2")

public ListgetUserInfos() {

Listids = new ArrayList<>();

for (int i = 0; i < 10; i++) {

ids.add(i);

}

Listusers = new ArrayList<>();

for (int i = 0; i < 10; i++) {

users.add(new User(i, “yyg-” + i));

}

return filterService.doFilter(ids, users);

}

}

我们启动浏览器进行测试,可以看到测试接口中只返回id为偶数的元素。

4. 代码结构

下图是上面案例的代码结构,请参考实现:

五. 利用动态权限实现权限控制

我们知道一个标准的RABC, 权限系统需要支持动态配置,Spring Security默认是在代码里约定好权限,真实的业务场景里通常需要可以支持动态配置角色访问权限,即在运行时去配置url对应的访问角色。

而Spring Security中的动态权限,主要是通过重写拦截器和决策器来进行实现,最简单的方法就是自定义一个Filter去完成权限判断。其实这里涉及到的代码,基本和Spring Security关系不大,主要是在传统的Filter进行实现,我这里就不再进行描述了,感兴趣的同学可以自行实现!

至此,我就给各位介绍了Spring Security中的4种进行权限控制的方式,各位可以结合自己的项目需求进行选择。

本文来自千锋教育,转载请注明出处。

以上是关于Spring Security系列之核心概念的主要内容,如果未能解决你的问题,请参考以下文章

Spring Security OAuth:源码解析之还是内味儿

爆破专栏丨Spring Security系列教程之Spring Security的四种权限控制方式

Spring Security OAuth:源码解析

Spring Cloud Gateway核心概念和工作原理-Part 1

Java实战之03Spring-03Spring的核心之AOP(Aspect Oriented Programming 面向切面编程)

Spring框架系列 - 深入浅出Spring核心之面向切面编程(AOP)