Spring Boot+Spring Security+Spring Social项目开发(八):Spring Security 控制授权、源码解析
github已经上传:https://github.com/13652493839/TiHom-Security
各位如果觉得博主写得还可以就可以给我star呀哈哈谢谢啦
Spring Boot+Spring Security+Spring Social项目开发(九):Spring Security授权表达式、重构配置方面的内容、数据库RBAC数据模型控制权限
Spring Boot+Spring Security+Spring Social项目开发(七):使用JWT替换默认Token、JWT实现SSO单点登录
Spring Boot+Spring Security+Spring Social项目开发(六):开发APP认证框架、Spring Security OAuth核心源码、重构三种登录方式、重构社交登录
Spring Boot+Spring Security+Spring Social项目开发(五):微信开发、绑定与解绑、Session管理、退出登录
Spring Boot+Spring Security+Spring Social项目开发(四):使用Spring Social开发第三方登录、QQ登录开发
Spring Boot+Spring Security项目开发(三):实现短信验证码登录
Spring Boot+Spring Security项目开发(二):Spring Security、实现图形验证码功能、实现”记住我”功能
Spring Boot+Spring Security项目开发(一):RESTful API介绍
Spring Security 控制授权
之前我们介绍的基本都是”你是谁”层面的开发,而这里实现的是”你能干什么”
Spring Security对授权的定义
有些人对这里授权的理解为不同权限进入系统就把不同权限所得到的界面的菜单按钮隐藏起来.这就是所谓的控制”不能看见”,但是真正的我们应该做的是”不能访问”,因为一个按钮对应的都是一个url,SpringSecurity应该实现”不能访问”,使用RBAC模型去实现用户体验的问题–>”不能看见”
- 普通用户
- 区分是否登录
- 我们在之前的代码中已经实现过了,在BrowserSecurityConfig类中的configure中
.authorizeRequests() //认证请求
.antMatchers(
SecurityConstants.DEFAULT_UNAUTHENTICATION_URL,
SecurityConstants.DEFAULT_LOGIN_PROCESSING_URL_MOBILE,
securityProperties.getBrowser().getLoginPage(),
SecurityConstants.DEFAULT_VALIDATE_CODE_URL_PREFIX+"/*",
securityProperties.getBrowser().getSignUpUrl(),
securityProperties.getBrowser().getSession().getSessionInvalidUrl(),
securityProperties.getBrowser().getSignOutUrl(),
"/user/regist")
.permitAll() //当我访问这个url的时候,我不需要身份认证就可以访问,其他的都需要认证
.anyRequest() //任何请求
.authenticated() //进行身份认证
.and()
.csrf().disable(); //防护的功能关闭
这段代码就只是过滤是否登录的用户
- 我们在之前的代码中已经实现过了,在BrowserSecurityConfig类中的configure中
- 区分简单角色(即普通用户与VIP用户)
- 而要做到区分登录用户后的角色分配,还是在上面那段代码中做修改,在.permitAll()后面加上
.antMatchers("/user").hasRole("ADMIN") //只有ADMIN角色能登录
这样就把非ADMIN权限访问的所有用户都拦截了;若要做到访问,要去demo中的MyUserDetailsService中的buildUser进行修改
AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_ADMIN")); //加了/user就可以访问,不加就访问不了
至于为什么使用”ROLE_”做开头后面再解释.
有两种情况:在rest服务中可能/user有POST也有GET请求,请求上代表资源(/{id})这时候怎么处理呢?
.antMatchers(HttpMethod.GET,"/user/*").hasRole("ADMIN") //只有ADMIN角色能登录
可以指定HttpMethod来指定GET和POST等等,/user/* 来指定所有/user相关的url请求
- 而要做到区分登录用户后的角色分配,还是在上面那段代码中做修改,在.permitAll()后面加上
- 区分是否登录
SpringSecurity源码解析
- FilterSecurityInterceptor,它是整个授权判断流程的主入口,请求发在这个过滤器上
- AccessDecisionManager访问决定的管理者,是一个接口,有一个抽象的实现和三个具体的实现;它在抽象的AbstractAccessDecisionManager管理着一组AccessDecisionVoter投票者,决策机制.SpringSecurity3以后WebExpressionVoter包办了SpringSecurity3以前多个voter的工作
- AbstractAccessDecisionManager又对应三种决定策略
- 1.AffirmativeBased:基于肯定,不管多少个voter投不过 ,只要有一个投通过就通过(Spring默认使用)
- 2.ConsensusBased:比较voter投通过和不通过的个数,哪个多就按那种决定来
- 3.UnanimousBased:不管有多少个voter投通过,只要有一个voter投不过请求就不通过
- FilterSecurityInterceptor会从SecurityConfig中把配置读出来封装成一组SecurityConfigAttribute的对象,这组对象里面就是整个系统的配置信息
- SecurityContextHolder就是当前用户所拥有的权限信息,这些信息封装在Authentication里面的
- 这两块数据加上FilterSecurityInterceptor封装的请求的信息(url等等),一共三份信息,传给AccessDecisionManager,再把这份信息传递给AccessDecisionVoter投票者,来进行投票是过还是不过,根据选择的逻辑汇总投票的结果给出过还是不过
源码流程
- 没登录去访问系统被拒绝的流程
- 进入FilterSecurityInterceptor–>doFilter方法,将请求、响应、后面的过滤器链全部封装到FilterInvacation类中–>invoke
- invoke判断这个请求之前有没有经过这个过滤器,有判断过的话直接往下走,没有的话就设FILTER_APPLIED值,下次判断就知道已经判断过了
- 接着进入beforeInvacation,先进行一些参数的判断,然后到下面这句代码,对应之前流程图中ConfigAttribute的处理
Collection<ConfigAttribute> attributes = this.obtainSecurityMetadataSource().getAttributes(object); - 进入getAttributes方法,for循环一个requestMap–>map中对应我们在SecurityConfig中的配置,对应的authorizeRequests()到authenticated()这段代码的配置;在for循环中找与当前请求相匹配的配置信息
- 同样SecurityContextHolder去getAuthentication()
- this.accessDecisionManager.decide(authenticated,object,attributes),object对于请求的信息,这里将三份信息传给AccessDecisionManager这个对象
- 然后扔给投票器去投票,若访问拒绝,则抛出异常
- 往链前面的ExceptionTranslationFilter抛,它接受后判断是否为访问拒绝异常,拿出Authentication判断是否是Anonymous(匿名)的,是就是因为没有做身份验证,sendStartAuthentication去认证;如果非匿名,会返回403界面
- hasRole会在传进来的role前加上ROLE_前缀,这就是为什么返回SocialUserDetails时要写”ROLE_ADMIN”而在SecurityConfig中的配置只需要写”ADMIN”
- 经过投票结果后就返回到beforeInvacation中去,往下就是调用rest的服务了