Spring Security

Spring提供了安全验证框架Spring Security

Spring Security是基于spring的应用程序提供声明式安全保护的安全性框架,它提供了完整的安全性解决方案,能够在web请求级别和方法调用级别处理身份证验证和授权.它充分使用了依赖注入和面向切面的技术

Spring security主要是从两个方面解决安全性问题:
**web请求级别:**使用servlet过滤器保护web请求并限制URL级别的访问
**方法调用级别:**基于Spring AOP保护方法调用,确保具有适当权限的用户采用访问安全保护的方法.
同样shiro框架也提供了这些功能
我们先来关注web请求级别的安全防护:

关键类:
**Authentication:**用户信息存储在SpringSecurity中的最终形态,这是一个接口,不同的登录方式需要构造不同的Authentication的实现。表单登录时就需要构造一个UsernamePasswordAuthenticationToken对象
**AuthenticationProvider:**一个AuthenticationProvider的实现只处理一种类型的Authentication对象,通过supports方法告诉AuthenticationManager他处理哪种类型的Authentication ,之后他会调用UserdetailService的实现去业务系统查找用户,校验密码等信息,全部成功后返回已授权的Authentication对象。

**AuthenticationManager:**全局唯一,他的作用是收集和管理系统中的所有的AuthenticatonProvider(Authentication对象的加工处理器)
FilterSecurityInterceptor是安全过滤拦截器,继承了AbstractSecurityInterceptor,security的功能基本上就是靠这个拦截器完成
**CustomUserService:**自定义类用户权限信息提供者,继承UserDetailsService,SpringSecurity用来查询用户信息的接口,他只有有一个loadUserByUsername方法,需要客户端实现这个接口来查询数据源中的用户信息,最后返回一个UserDetail对象(他是SpringSecurity对用户信息的封装,可以用自己的User类去实现这个接口,也可以用他默认的UserDetail的实现

**AuthenticationEntryPoint:**安全异常处理点,定义了没有权限时候的异常处理策略,在ExceptionTranslationFilter中使用到AuthenticationEntryPoint,当ExceptionTranslationFilter截获异常时,就会调用AuthenticationEntryPoint 的commence
在这里我们可以对异常进行自定义处理

我们明白了security安全处理的关键类的作用,接下来我们来了解一下security安全机制的执行流程:
security的主要功能其实就是由各个过滤器实现的,主要有用户登录得认证管理拦截器
下面我们使用用户账号密码登录请求过程来梳理security的处理流程:(认证流程)
在这里插入图片描述
在过滤器UsernamePasswordAuthenticationFilter中组装Token,实际上这里的组装的Token是未验证的,然后通过AuthenticationManager遍历所有的Provider寻找处理对应的token的Provider,Provider在处理token的时候会调动我们自己处理的UserDetailService,当然Security内部要调用我们的自定义类,需要我们在配置管理器中声明我们提供了哪些实现类

请求的共享:(认证请求和服务请求之间用户信息的共享)
在这里插入图片描述
之后的服务请求Security会SecurityContextPersistenceFilter会先查询session中是否已经认证了。出去的时候回将认证信息存入session。这样就解决了请求的共享的问题
在这里插入图片描述

请求拦截链:

在这里插入图片描述
为了方便用户所以我们需要记住用户,当用户认证成功之后,在一段时间内不需要再次认证,可以直接访问某一个受保护的服务,这个请求在经过过滤器链的时候会经过RemberMeAuenticationFilter (读取Cookie中的Token)给RemberMeService,RemberMeService会根据Token到数据库里面去查。如果有记录,就会把Username用户名取出来,取出来之后会调用UserDetailsService,获取用户信息,然后把用户信息放入到SecurityContext里面。

AuthenticationManager和Provider的关系
在这里插入图片描述

用户登陆,会被AuthenticationProcessingFilter拦截(即认证管理),调用AuthenticationManager的,AuthenticationManager会调用Provider来获取用户验证信息,Provider调用userDetailService等提供用户权限信息的服务,获取到用户的权限信息,然后判断权限信息是否符合要求,负责则对Authentication对象进行授权
最后将经过授权的Authentication对象简单包装到SecurityContext中(其实就是重写了hashcode和equesls方法),最后将SecurityContext放在SecurityContextHolder中的TheadLocal变量中,用于在线程内共享用户信息,最后经过一个SecurityContextPersistenceFilter过滤器,他在处在整个过滤器链的最前端,他有两个作用,一个是请求进来时,检查session中有无SecurityContext,若有放在线程里,请求出去的时候,检查线程里有无SecurityContext,有的话放在session里,这样,就完成了整个登录流程。
在这里插入图片描述

UserDetailsService需要我们自定义实现:
当有用户登陆时,spring security会调用UserDetailsService的loadUserByUsername方法,并把用户输入的账号传进来,方法的返回值是一个UserDetails对象
方法不会将密码传进来,因为这个方法不会做用户名和密码的校验,该方法需要做的是根据用户名从数据库中查出来用户的信息,然后将其交给spring security
spring security会根据这个信息和用户输入的账号密码来校验登陆是否成功,如果登陆成功,那么spring security会将用户信息、权限等保存在内存中,以便后边使用。也就是说,登陆校验是由spring security来做的,不需要我们显式的处理。

自定义用户登录逻辑:
创建MyUserDetailService实现UserDetailService接口,就可以在此类中实现自定义的用户登录逻辑
实现WebSecurityConfigurerAdapter可以进行我们自己的配置

应用自定义controller和安全框架结合:
在这里插入图片描述

短信登录请求流程图:
在这里插入图片描述

接下来我们看一下security提供的方法级别的安全防护:
保护方法调用
spring Security支持4种方法级别安全性的方法:

1.使用@Secured注解方法,这是spring自带的注解方法。@Secured("")内部的字符串不具有SpEL特性,只能是具体的权限。
2.使用@JSR-250 @RelosAllowed注解的方法。作用和使用方法与@Secured一样,不同在于它不是spring框架的,所以可以做到和spring框架的解耦。
3.使用Spring 方法调用前和调用后注解方法。这些方法支持SpEL.
4.匹配一个或多个明确声明的切点方法。

@Secured和 @RelosAllowed
Secured注解会shiy9ong一个String数组作为参数,每个String值是一个权限,diaoy9ong方法至少需要具备一个权限

@Secured("ROLE_ADMIN")
public void addUser(User user){//只有具备ROLE_ADMIN权限的用户才可以访此方法
}

RolesAllowed注解在各个方面基本上都是一致的,区别只在于一个是spring独有的一个是基于Java标准的

@RolesAllowed("ROLE_ADMIN")
public void updateUser(User user){
}

尽管Secured,RolesAllowed两个注解在拒绝没有权限的用户访问方面表现不错,但是这也是它们可以做到的所有事情了,但是有时候安全性约束不仅仅涉及到用户是否有权限

接下来我们使用可以根据SpEL表达式来实现访问决策的注解,借助SpEl表达式我们可以编写出更高级的安全约束,例如:
对普通用户发布博客做字数限制,而对VIP用户则没有字数限制
但在使用表达式注解之前我们需要先启用这种验证方式:如下启用方法调用前后的注解

@EnableGlobalMethodSecurity(prePostEnabled=true)

2.使用Spring 方法调用前和调用后注解方法,可以使用SpEL方法的注解有四种:
两个权限方法,两个过滤器方法
@PreAuthorize: 在方法调用前,基于表达式计算结果来限制方法访问
@PostAuthorize: 允许方法调用,但是如果表达式结果为fasle则抛出异常
@PostFilter :允许方法调用,但必须按表达式过滤方法结果。
@PreFilter:允许方法调用,但必须在进入方法前过滤输入值
例如:@PreAuthorize,当一个方法声明了这个注解,在方法执行之前会判断注解中表达式的值,当值为true的时候才可以访问方法

@PreAuthorize("#blog.text.length <= 140")
public void addBlog(Blog blog){
   //如果博客字数小于140可以访问方法
}

//returnObject可以获取返回对象user,判断user属性username是否和访问该方法的用户对象的用户名一样。不一样则抛出异常

@PostAuthorize("returnObject.user.username==principal.username")
public User getUser(int userId){
    return user;    
}

但是需要注意保证方法验证失败,方法内部的操作可以回滚

上面两个spring方法调用前后注解,完成了不同的工作,但都是使用SpEL表达式访问方法的参数或者返回值,然后根据表达式的结果执行某些安全操作

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值