任何的流程必须通过语言能够描述,才达到最终效果
认证和授权总流程
AnonymousAuthenticationFilter在所有认证过滤器最后,判断SpringSecurityConetxt中是否存在Authentication,若不存在则设置一个AnonymousAuthentication。
认证技术技术选型
企业中大多使用Shiro框架和Spring Security进行用户身份认证,而实现SSO的技术选型又有Spring Security Oauth2.0和CAS,它们都可以很好的实现单点登录,我对Oauth2.0比较推介,因为它不仅在现有框架Spring Security下进行很好的实现,而且与其他技术的兼容性比较好,最重要的是Spring Cloud对它有现成的实现,搭建微服务认证中心非常合适。而CAS代码实现是是通过一些零散的过滤器实现,而且对前后端分离不好实现。
CAS对各个子系统的资源请求进行认证处理,类似于Oauth2.0基于授权码的授权模式,客户端应用不接触用户的账号密码信息,只有认证中心能接受用户的用户名密码等安全信息,其他系统不提供登录入口,只接受认证中心的间接授权。而Oauth2.0提供的账号密码授权模式客户端应用是接触到用户的安全信息的,但只适合对APP的认证处理。当用户再次请求时,它们都需要通过认证中心验证令牌的有效性,但是基于JWT的oauth2.0协议可以在令牌中取出用户信息即可,省去了再次验证信息
具体来讲,有单机版的认证,多机版的认证,基于微服务的认证。
单机版的认证可以通过框架直接完成
多机版的认证可以通过Spring Security整合Oauth2.0协议,也可以整合CAS来实现,也可使用CAS单独实现
微服务的认证其实就是基于网关的认证和授权,所有对微服务的请求都通过网关进行细粒度的控制,通过认证中心进行实际的认证和授权,网关除了解决认证和授权,还进行日志收集和熔断限流
cas基本流程
https://blog.csdn.net/anumbrella/category_7765386.html
首次登陆流程
- 首先用户访问系统1受保护的资源,系统1发现未登陆,跳转至SSO认证中心
- SSO认证中心发现用户未登录,将用户引导至登录页面
- 用户输入用户名和密码提交至SSO认证中心
- SSO认证中心校验用户信息,创建用户与SSO认证中心之间的会全局会话,同时创建授权令牌
- SSO认证中心带着令牌跳转回最初的请求地址(系统1)
- 系统1拿到令牌,去SSO认证中心校验令牌是否有效
- SSO认证中心校验令牌,返回有效,注册系统1的地址
- 系统1使用该令牌创建与用户的会话,称为局部会话,返回给用户受保护资源
再次登陆流程
- 用户访问系统2受保护的资源
- 系统2发现用户未登录,跳转至SSO认证中心,并将自己的地址作为参数传递过去
- SSO认证中心发现用户已登录,跳转回系统2的地址,并附上令牌
- 系统2拿到令牌,去SSO认证中心校验令牌是否有效
- SSO认证中心校验令牌,返回有效,注册系统2地址
- 系统2使用该令牌创建与用户的局部会话,返回给用户受保护资源
注销流程
- 系统1根据用户与系统1建立的会话id拿到令牌,向SSO认证中心发起注销请求
- SSO认证中心校验令牌有效,销毁全局会话,同时取出所有用此令牌注册的系统地址
- SSO认证中心向所有注册系统发起注销请求
- 各注册系统接收SSO认证中心的注销请求,销毁局部会话
- SSO认证中心引导用户至登录页面
TGC:用户持有的令牌,表示成功登陆casClient web应用
TGT:CAS为用户签发的全局令牌,构建全局会话
ST(TGT签发):TGT签发的某服务的令牌,构建局部会话
ST是TGT签发的。用户在CAS上认证成功后生成TGT,然后用TGT签发一个ST,ST包含TGT属性,然后redirect到客户端应用
引入app的代理,为用户去申请PT,PT代表用户成功登陆casClient app应用
a) PGT(ST签发):代理服务持有的令牌,表示用户成功登陆某一代理服务,可以代理应用为其申请PT
b) PT:CAS为用户签发的访问app服务的令牌,表示成功登陆casClient app应用
cas实现相关
如果要验证其他信息,比如邮箱,手机号,但是邮箱,手机信息在另一个数据库,还有在一段时间内同一IP输入错误次数限制等。这里就需要我们自定义认证策略,自定义CAS的web认证流程
通过拦截请求获取到Handler,来实现自定义认证策略
自定义登陆逻辑
- 通过继承AbstractUsernamePasswordAuthenticationHandler实现用户名密码登陆,或者AbstractPreAndPostProcessingAuthenticationHandler实现其他数据的处理逻辑
- 通过继承AuthenticationEventExecutionPlanConfigurer注册自定义的认证逻辑
cas service配置
- 配置内容主要是:服务访问配置,服务属性配置,服务到期策略配置
- 配置存储主要是:JSON file,Redis,JPA
cas service管理
- 启用线程项目cas-management-overlay进行服务管理
- 自定义服务管理程序,通过实现ServiceManager接口
自定义用户登陆界面
认证登陆页面耦合于cas server
- 根据配置的cas service存储方式,覆盖默认的登陆界面
- 自定义验证码,使用Google的kaptcha类
SpringBoot配置cas client
- 配置拦截请求和退出的路径,并注入自定义配置类
- 注入FilterRegistrationBean,然后通过它配置登陆认证过滤器,登出过滤器,ticket效验过滤器,获取登陆信息
SpringSecurity基本流程
认证流程
认证请求到达SecurityContextPersistenceFilter检查是否有session,若有则直接返回登陆成功,否则进入UsernamePasswordFilter进行账号密码认证,它底层通过AuthenticationManager遍历可用的AuthentcationProvider进行具体的认证操作,底层通过UserDetailsService根据用户名称获取用户信息与表单填入的进行匹配,若认证成功则将用户信息放SecurityContextHolder
实现remember功能
UsernamePasswordAuthenticationFilter认证成功后,在成功处理器就会自动调用RememberMeService的方法创建token和用户名一起存储到数据库中,然后将token写到浏览器的Cookie中。
当用户再次访问系统时,被RememberMeAuthenticationFilter过滤器所拦截,它读取cookie中的token,通过RememberService从数据库中查出token对应的用户名,然后通过UserDetailService再根据用户名查询用户信息,最后放到SecuritContext里。
图形验证码效验
- 自定义控制器生成图形验证码,并生成图片返回前端,然后将其保存到redis和session中
- 创建验证码过滤器,判断用户填写的验证码和系统生成的验证码是否匹配,若不匹配则抛异常
短信验证码登陆效验
- 自定义控制器生成短信验证码,将其发送给用户,将验证码保存到redis或者session中
- 自定义过滤器拦截短信登陆请求,然后自定义AuthenticationProvider返回已认证Authentication
- 自定义短信验证码过滤器,判断用户填写的短信验证码和存储中的短信验证吗是否匹配,若不匹配则抛异常
|——为何不在provider进行匹配短信验证码呢??为了使的验证短信验证码这个流程可以复用,比如让支付流程调用等
|——有两条线,一条是根据手机号进行身份认证(只保证系统中存在此用户),另一条是验证短信验证码是否匹配的逻辑
使用SpringSocial实现第三方登陆
- 用户访问客户端前端,浏览器将请求导向认证服务器进行认证
- 用户同意授权,携带授权码返回给客户端前端
- 客户端后台携带授权码申请令牌
- 认证服务器发放令牌,客户端后台访问服务提供商的用户数据,携带令牌返回客户端前端
Oauth2.0实现认证服务器
相比CAS,第三方
- Oauth生成令牌流程(认证服务器)
客户端应用向认证服务器申请令牌,TokenEndpoint通过ClientDetailsService从存储中获取注册的第三方应用信息,然后将第三方用户信息和用户请求信息封装为Oauth2Authentication,AuthorizationServerTokenService创建Oatuth2AccessToken令牌,然后TokenStore将其存到某存储器中 - Oauth效验令牌流程(资源服务器)
客户端携带令牌访问请求到达Oauth2AuthenticationProcessFilter,tokenExtractor从request中获取Authentication,OAuth2AuthenticationManager通过tokenService查询token对应的OAuth2Authentication对应,若登陆成功将Authentication放到SpringSecurityConetxt中。
Oauth2.0重构用户名密码登陆
在成功处理器中,封装Oauth2Authentication,然后AuthorizationServerTokenService创建Oatuth2AccessToken令牌,然后TokenStore将其存到某存储器中
基于Oauth2.0 jwt实现SSO
- 创建认证服务器,配置认证服务器客户端信息,配置token存储策略,配置tokenkey需要身份认证
- 创建rest服务器1,@EnableOauth2Sso,配置客户端配置(客户端信息,访问认证服务器url,请求令牌url,获取密钥url)
- 创建rest服务器2,@EnableOauth2Sso,配置客户端配置(客户端信息,访问认证服务器url,请求令牌url,获取密钥url)
Session管理
- session超时处理:server.session.timeout= 最少一分钟,可配置失效处理控制器
- session并发控制
用户在其他浏览器登陆,踢掉前面那个:maximumSession(1)
用户不允许在其他地方登陆:maxSessionPreventLogin(true) - 集群session处理
spring.session.store-type=REDIS - 退出登陆
访问/logout后,当前session失效,清空remember-me记录,清空securityConetxt,重定向到登录页
logoutUrl()配置退出控制器,logoutSuccessHandler()配置退出重定向url,deleteCookies()删除浏览器cookies
授权
系统配置信息:权限规则基本不变,antMatchers(方法,路由).hasRole(角色)
用户权限信息 :权限规则灵活变化
SpringSecurity授权源码流程
请求信息vs系统配置权限vs用户拥有的权限——>AccessDecisionManager(策略)——>AccessDecisionVoter(WebExpressionVoter)多个投票者进行投票
SpringSecurity将权限转换成一个权限表达式,然后通过WebExpressionVoter进行评估,只要有一个通过就通过
连用表达式
基于配置文件进行权限控制
- 只区分是否登录or只区分简单角色
- 权限规则基本不变
问题:在使用配置文件配置权限时,安全模块事先并不知道业务模块具体有哪些API需要权限控制,怎么将配置信息进行全局管理呢???
AuthorizeConfigManager收集所有AuthorizeConfigProvider的实现,然后进行统一配置
基于角色的权限控制
角色众多,随着公司发展权限不断变化
资源表(开发人员维护):菜单,按钮及其URL
用户表,角色表,用户-角色表,角色-权限表
.access("@rbacService.hasPermission(request,authentication)")
.access("#rbacService.hasPermission(request,authentication)") //需要配置相应的权限表达式处理器
怎样对接自己写的权限模块 和 SpringSecurity
a)定义一个RbacService接口,根据request和Authentication判断是否有权限
b)实现授权服务方法,根据用户名查询所拥有权限的所有URL,只要有一个匹配request.getRequestURI()就判定权限通过
c)实现AuthorizeConfigProvider,将自己自定义的权限服务,以表达式的方式配置进去
d) 实现一个AuthorizeConfigManager,注入所有模块的AuthorizeConfigProvider,然后将授权配置管理器配置到SecurityCofig默认配置