旅行网站定制公司,佛山网站制作网页,询价报价单模板,如何做网站插件作者#xff1a; [Aoho’s Blog] 引言#xff1a; 本文系《认证鉴权与API权限控制在微服务架构中的设计与实现》系列的第一篇#xff0c;本系列预计四篇文章讲解微服务下的认证鉴权与API权限控制的实现。 1. 背景 最近在做权限相关服务的开发#xff0c;在系统微服务化后 [Aoho’s Blog] 引言 本文系《认证鉴权与API权限控制在微服务架构中的设计与实现》系列的第一篇本系列预计四篇文章讲解微服务下的认证鉴权与API权限控制的实现。 1. 背景 最近在做权限相关服务的开发在系统微服务化后原有的单体应用是基于session的安全权限方式不能满足现有的微服务架构的认证与鉴权需求。微服务架构下一个应用会被拆分成若干个微应用每个微应用都需要对访问进行鉴权每个微应用都需要明确当前访问用户以及其权限。尤其当访问来源不只是浏览器还包括其他服务的调用时单体应用架构下的鉴权方式就不是特别合适了。在微服务架构下要考虑外部应用接入的场景、用户–服务的鉴权、服务–服务的鉴权等多种鉴权场景。 最近在做权限相关服务的开发在系统微服务化后原有的单体应用是基于session的安全权限方式不能满足现有的微服务架构的认证与鉴权需求。微服务架构下一个应用会被拆分成若干个微应用每个微应用都需要对访问进行鉴权每个微应用都需要明确当前访问用户以及其权限。尤其当访问来源不只是浏览器还包括其他服务的调用时单体应用架构下的鉴权方式就不是特别合适了。在微服务架构下要考虑外部应用接入的场景、用户–服务的鉴权、服务–服务的鉴权等多种鉴权场景。 比如用户A访问User ServiceA如果未登录则首先需要登录请求获取授权token。获取token之后A将携带着token去请求访问某个文件这样就需要对A的身份进行校验并且A可以访问该文件。 最近在做权限相关服务的开发在系统微服务化后原有的单体应用是基于session的安全权限方式不能满足现有的微服务架构的认证与鉴权需求。微服务架构下一个应用会被拆分成若干个微应用每个微应用都需要对访问进行鉴权每个微应用都需要明确当前访问用户以及其权限。尤其当访问来源不只是浏览器还包括其他服务的调用时单体应用架构下的鉴权方式就不是特别合适了。在微服务架构下要考虑外部应用接入的场景、用户–服务的鉴权、服务–服务的鉴权等多种鉴权场景。 比如用户A访问User ServiceA如果未登录则首先需要登录请求获取授权token。获取token之后A将携带着token去请求访问某个文件这样就需要对A的身份进行校验并且A可以访问该文件。 为了适应架构的变化、需求的变化auth权限模块被单独出来作为一个基础的微服务系统为其他业务service提供服务。 2. 系统架构的变更 单体应用架构到分布式架构简化的权限部分变化如下面两图所示。 单体应用架构到分布式架构简化的权限部分变化如下面两图所示。 1单体应用简化版架构图 单体应用架构到分布式架构简化的权限部分变化如下面两图所示。 1单体应用简化版架构图 [ 单体架构 2分布式应用简化版架构图 分布式架构 分布式架构特别是微服务架构的优点是可以清晰的划分出业务逻辑来让每个微服务承担职责单一的功能毕竟越简单的东西越稳定。 但是微服务也带来了很多的问题。比如完成一个业务操作需要跨很多个微服务的调用那么如何用权限系统去控制用户对不同微服务的调用对我们来说是个挑战。当业务微服务的调用接入权限系统后不能拖累它们的吞吐量当权限系统出现问题后不能阻塞它们的业务调用进度当然更不能改变业务逻辑。新的业务微服务快速接入权限系统相对容易把控那么对于公司已有的微服务如何能不改动它们的架构方式的前提下快速接入对我们来说也是一大挑战。 3. 技术方案 这主要包括两方面需求其一是认证与鉴权对于请求的用户身份的授权以及合法性鉴权其二是API级别的操作权限控制这个在第一点之后当鉴定完用户身份合法之后对于该用户的某个具体请求是否具有该操作执行权限进行校验。 3.1 认证与鉴权 对于第一个需求笔者调查了一些实现方案 分布式Session方案 分布式Session方案 分布式会话方案原理主要是将关于用户认证的信息存储在共享存储中且通常由用户会话作为 key 来实现的简单分布式哈希映射。当用户访问微服务时用户数据可以从共享存储中获取。在某些场景下这种方案很不错用户登录状态是不透明的。同时也是一个高可用且可扩展的解决方案。这种方案的缺点在于共享存储需要一定保护机制因此需要通过安全链接来访问这时解决方案的实现就通常具有相当高的复杂性了。 基于OAuth2 Token方案 基于OAuth2 Token方案 随着 Restful API、微服务的兴起基于Token的认证现在已经越来越普遍。Token和Session ID 不同并非只是一个 key。Token 一般会包含用户的相关信息通过验证 Token 就可以完成身份校验。用户输入登录信息发送到身份认证服务进行认证。AuthorizationServer验证登录信息是否正确返回用户基础信息、权限范围、有效时间等信息客户端存储接口。用户将 Token 放在 HTTP 请求头中发起相关 API 调用。被调用的微服务验证Token。ResourceServer返回相关资源和数据。 这边选用了第二种方案基于OAuth2 Token认证的好处如下 服务端无状态Token 机制在服务端不需要存储 session 信息因为 Token 自身包含了所有用户的相关信息。性能较好因为在验证 Token 时不用再去访问数据库或者远程服务进行权限校验自然可以提升不少性能。现在很多应用都是同时面向移动端和web端OAuth2 Token机制可以支持移动设备。OAuth2与Spring Security结合使用有提供很多开箱即用的功能大多特性都可以通过配置灵活的变更。最后一点也很重要Spring Security OAuth2的文档写得较为详细。oauth2根据使用场景不同分成了4种模式 授权码模式authorization code简化模式implicit密码模式resource owner password credentials客户端模式client credentials对于上述oauth2四种模式不熟的同学可以自行百度oauth2阮一峰的文章有解释。常使用的是password模式和client模式。 3.2 操作权限控制 对于第二个需求笔者主要看了Spring Security和Shiro。 Shiro Shiro Shiro是一个强大而灵活的开源安全框架能够非常清晰的处理认证、授权、管理会话以及密码加密。Shiro很容易入手上手快控制粒度可糙可细。自由度高Shiro既能配合Spring使用也可以单独使用。 Spring Security Spring Security Spring社区生态很强大。除了不能脱离SpringSpring Security具有Shiro所有的功能。而且Spring Security对Oauth、OpenID也有支持,Shiro则需要自己手动实现。Spring Security的权限细粒度更高。但是Spring Security太过复杂。 看了下网上的评论貌似一边倒向Shiro。大部分人提出的Spring Security问题就是比较复杂难懂文档太长。不管是Shiro还是Spring Security其实现都是基于过滤器对于自定义实现过滤器我想对于很多开发者并不是很难但是这需要团队花费时间与封装可用的jar包出来对于后期维护和升级以及功能的扩展。很多中小型公司并不一定具有这样的时间和人力投入这件事。笔者综合评估了下复杂性与所要实现的权限需求以及上一个需求调研的结果既然Spring Security功能足够强大且稳定最终选择了Spring Security。 4. 系统架构 4.1 组件 Auth系统的最终使用组件如下 OAuth2.0 JWT Token
Spring Security
Spring boot 4.2 步骤 主要步骤为 配置资源服务器和认证服务器配置Spring Security上述步骤比较笼统对于前面小节提到的需求属于Auth系统的主要内容笔者后面会另写文章对应讲解。 4.3 endpoint 提供的endpoint /oauth/token?grant_typepassword #请求授权token/oauth/token?grant_typerefresh_token #刷新token/oauth/check_token #校验token/logout #注销token及权限相关信息 4.4 maven依赖 主要的jar包pom.xml文件如下 dependencygroupIdcom.auth0/groupIdartifactIdjava-jwt/artifactIdversion2.2.0/version/dependencydependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-security/artifactIdversion1.2.1-SNAPSHOT/version/dependencydependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-oauth2/artifactIdversion1.2.1-SNAPSHOT/version/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactIdexclusionsexclusiongroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-tomcat/artifactId/exclusion/exclusions/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-jersey/artifactIdversion1.5.3.RELEASE/version/dependency 4.5 AuthorizationServer配置文件 AuthorizationServer配置主要是覆写如下的三个方法分别针对endpoints、clients、security配置。 Overridepublic void configure(AuthorizationServerSecurityConfigurer security) throws Exception {security.tokenKeyAccess(permitAll()).checkTokenAccess(isAuthenticated());}Overridepublic void configure(ClientDetailsServiceConfigurer clients) throws Exception {//配置客户端认证clients.withClientDetails(clientDetailsService(dataSource));}Overridepublic void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { //配置token的数据源、自定义的tokenServices等信息endpoints.authenticationManager(authenticationManager).tokenStore(tokenStore(dataSource)).tokenServices(authorizationServerTokenServices()).accessTokenConverter(accessTokenConverter()).exceptionTranslator(webResponseExceptionTranslator);} 4.6 ResourceServer配置 资源服务器的配置覆写了默认的配置。为了支持logout这边自定义了一个CustomLogoutHandler并且将logoutSuccessHandler指定为返回http状态的HttpStatusReturningLogoutSuccessHandler。 Overridepublic void configure(HttpSecurity http) throws Exception {http.csrf().disable().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().requestMatchers().antMatchers(/**).and().authorizeRequests().antMatchers(/**).permitAll().anyRequest().authenticated().and().logout().logoutUrl(/logout).clearAuthentication(true).logoutSuccessHandler(new HttpStatusReturningLogoutSuccessHandler()).addLogoutHandler(customLogoutHandler()); 4.7 执行endpoint 首先执行获取授权的endpoint。method: post
url: http://localhost:12000/oauth/token?grant_typepassword
header:
{Authorization: Basic ZnJvbnRlbmQ6ZnJvbnRlbmQ,Content-Type: application/x-www-form-urlencoded
}
body:
{username: keets,password: ***
} 上述构造了一个post请求具体请求写得很详细。username和password是客户端提供给服务器进行校验用户身份信息。header里面的Authorization是存放的clientId和clientSecret经过编码的字符串。 上述构造了一个post请求具体请求写得很详细。username和password是客户端提供给服务器进行校验用户身份信息。header里面的Authorization是存放的clientId和clientSecret经过编码的字符串。 返回结果如下 { access_token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJYLUtFRVRTLVVzZXJJZCI6ImQ2NDQ4YzI0LTNjNGMtNGI4MC04MzcyLWMyZDYxODY4ZjhjNiIsImV4cCI6MTUwODQ0Nzc1NiwidXNlcl9uYW1lIjoia2VldHMiLCJqdGkiOiJiYWQ3MmIxOS1kOWYzLTQ5MDItYWZmYS0wNDMwZTdkYjc5ZWQiLCJjbGllbnRfaWQiOiJmcm9udGVuZCIsInNjb3BlIjpbImFsbCJdfQ.5ZNVN8TLavgpWy8KZQKArcbj7ItJLLaY1zBRaAgMjdo, token_type: bearer,refresh_token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJYLUtFRVRTLVVzZXJJZCI6ImQ2NDQ4YzI0LTNjNGMtNGI4MC04MzcyLWMyZDYxODY4ZjhjNiIsInVzZXJfbmFtZSI6ImtlZXRzIiwic2NvcGUiOlsiYWxsIl0sImF0aSI6ImJhZDcyYjE5LWQ5ZjMtNDkwMi1hZmZhLTA0MzBlN2RiNzllZCIsImV4cCI6MTUxMDk5NjU1NiwianRpIjoiYWE0MWY1MjctODE3YS00N2UyLWFhOTgtZjNlMDZmNmY0NTZlIiwiY2xpZW50X2lkIjoiZnJvbnRlbmQifQ.mICT1-lxOAqOU9M-Ud7wZBb4tTux6OQWouQJ2nn1DeE,expires_in: 43195,scope: all,X-KEETS-UserId: d6448c24-3c4c-4b80-8372-c2d61868f8c6,jti: bad72b19-d9f3-4902-affa-0430e7db79ed,X-KEETS-ClientId: frontend
} 可以看到在用户名密码通过校验后客户端收到了授权服务器的response主要包括access* token、refresh* token。并且表明token的类型为bearer过期时间expires_in。笔者在jwt token中加入了自定义的info为UserId和ClientId。 2.鉴权的endpoint method: post
url: http://localhost:12000/oauth/check_token
header:
{Authorization: Basic ZnJvbnRlbmQ6ZnJvbnRlbmQ,Content-Type: application/x-www-form-urlencoded
}
body:
{token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJYLUtFRVRTLVVzZXJJZCI6ImQ2NDQ4YzI0LTNjNGMtNGI4MC04MzcyLWMyZDYxODY4ZjhjNiIsImV4cCI6MTUwODQ0Nzc1NiwidXNlcl9uYW1lIjoia2VldHMiLCJqdGkiOiJiYWQ3MmIxOS1kOWYzLTQ5MDItYWZmYS0wNDMwZTdkYjc5ZWQiLCJjbGllbnRfaWQiOiJmcm9udGVuZCIsInNjb3BlIjpbImFsbCJdfQ.5ZNVN8TLavgpWy8KZQKArcbj7ItJLLaY1zBRaAgMjdo
} 上面即为check_token请求的详细信息。需要注意的是笔者将刚刚授权的token放在了body里面这边可以有多种方法此处不扩展。 {X-KEETS-UserId: d6448c24-3c4c-4b80-8372-c2d61868f8c6,user_name: keets,scope: [all],active: true,exp: 1508447756,X-KEETS-ClientId: frontend,jti: bad72b19-d9f3-4902-affa-0430e7db79ed,client_id: frontend
} 校验token合法后返回的response如上所示。在response中也是展示了相应的token中的基本信息。 3.刷新token 3.刷新token 由于token的时效一般不会很长而refresh* token一般周期会很长为了不影响用户的体验可以使用refresh* token去动态的刷新token。 method: post
url: http://localhost:12000/oauth/token?grant_typerefresh_tokenrefresh_tokeneyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJYLUtFRVRTLVVzZXJJZCI6ImQ2NDQ4YzI0LTNjNGMtNGI4MC04MzcyLWMyZDYxODY4ZjhjNiIsInVzZXJfbmFtZSI6ImtlZXRzIiwic2NvcGUiOlsiYWxsIl0sImF0aSI6ImJhZDcyYjE5LWQ5ZjMtNDkwMi1hZmZhLTA0MzBlN2RiNzllZCIsImV4cCI6MTUxMDk5NjU1NiwianRpIjoiYWE0MWY1MjctODE3YS00N2UyLWFhOTgtZjNlMDZmNmY0NTZlIiwiY2xpZW50X2lkIjoiZnJvbnRlbmQifQ.mICT1-lxOAqOU9M-Ud7wZBb4tTux6OQWouQJ2nn1DeE
header:
{Authorization: Basic ZnJvbnRlbmQ6ZnJvbnRlbmQ
} 其response和/oauth/token得到正常的相应是一样的此处不再列出。 4.注销token method: get
url: http://localhost:9000/logout
header:
{Authorization: bearereyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1MDgzMzkwNTQsIlgtU0lNVS1Vc2VySWQiOiIwOGFhMTYxYi1lYjI3LTQ2NjAtYjA1MC1lMDc5YTJiODBhODMiLCJ1c2VyX25hbWUiOiJrZWV0cyIsImp0aSI6IjJhNTQ4NjY2LTRjNzEtNGEzNi1hZmY0LTMwZTI1Mjc0ZjQxZSIsImNsaWVudF9pZCI6ImZyb250ZW5kIiwic2NvcGUiOlsibWVua29yIl19.rA-U2iXnjH0AdPaGuvSEJH3bTth6AT3oQrGsKIams30
} 注销成功则会返回200注销端点主要是将token和SecurityContextHolder进行清空。 5. 总结 本文是《认证鉴权与API权限控制在微服务架构中的设计与实现》系列文章的总述从遇到的问题着手介绍了项目的背景。通过调研现有的技术并结合当前项目的实际确定了技术选型。最后对于系统的最终的实现进行展示。后面将从实现的细节讲解本系统的实现。敬请期待后续文章。 参考 理解OAuth 2.0微服务API级权限的技术架构微服务架构下的安全认证与鉴权我的官网 我的官网http://guan2ye.com 我的CSDN地址http://blog.csdn.net/chenjianandiyi 我的简书地址http://www.jianshu.com/u/9b5d1921ce34 我的githubhttps://github.com/javanan 我的码云地址https://gitee.com/jamen/ 阿里云优惠券https://promotion.aliyun.com/ntms/act/ambassador/sharetouser.html?userCodevf2b5zldutm_sourcevf2b5zld 阿里云教程系列网站http://aliyun.guan2ye.com 我的开源项目spring boot 搭建的一个企业级快速开发脚手架