个人项目相关问题
Restful
Eureka注册中心 – 微服务管理
Eureka 在项目中的部署使用
- 微服务上部署 Eureka Client,完成服务的注册和发现
- 添加eureka依赖
- 在application.yml配置eureka
- 在启动类上加
@EnableDiscoveryClient
注解
- 创建 Eureka Server 服务端,负责管理微服务节点信息和状态
- 微服务从 Server 调取另一个微服务的地址,远程调用
Feign 远程调用
- Feign 集成了 Ribbon,从而实现客户端负载均衡调用
- 启动类添加
@EnableFeignClients
注解 - 微服务要使用其他微服务时,通过
@FeignClient(value = '')
指定要调用的微服务名称,Feign以此从注册中心获取该服务,通过负载均衡算法进行远程调用。 - 在接口方法中使用
@GetMapping("")
,指定调用的url, - 在Service中实现远程微服务方法的调用。
负载均衡
LoadBalanceClient 客户端负载均衡
课程预览技术方案
主要是对课程详情页的预览。
- 需要尽量快的页面访问速度
- 使用 Nginx 作为 web 服务器,直接访问由 CMS 服务生成的 html 静态页面,性能很不错(3000/s)。需要将 CMS 静态化页面储存到 Nginix 中。少量动态数据通过访问服务端获取。
- 大量静态页面,也会增加维护难度。
- 如果使用 Tomcat 作为服务器访问,虽然 Redis 缓存可以加快缓存速度,但是 Tomcat 高并发量访问时性能不高(300/s)。
一些细节
- 实际运行环境中,应该部署多个 Eureka Server, 保证服务的高可用
ElasticSearch
- elasticsearch 是一个基于 Lucene 的高扩展的分布式搜索服务器,支持开箱即用。
- elasticsearch 隐藏了 Lucene 的复杂性,对外提供Restful 接口来操作索引、搜索。
- 扩展性好,可以部署上百台服务器集群
- 即使索引数据量很大,也能快速搜索(近实时)
索引结构
逻辑结构是倒排索引表。包括:
- 文档中所有不重复词组成的分词列表
- 搜索的文档(Document 形式)
- 分词和Document的关联
之所以是一个倒排索引,含义是通过关键词查找相应的文档,通过文档匹配度高低得到一个文档列表;而正常的查找是在文档里找关键字,最后得到的是关键字。
搜索方法
- 用户在前端搜索关键字
- 前端通过http方式请求项目服务端
- 服务端通过 Http RESTful 请求 ES 集群进行搜索
- ES 从索引库检索数据
ES 使用流程
- 创建索引库
- 创建映射
- 创建文档
- 搜索文档
以上这些步骤实际项目中通过 ES 提供的 Java API 实现。
一些细节
- 使用IK分词器进行中文分词,索引时使用ik_max_word进行细粒度分词;搜索时使用ik_smart提高搜索精确性
- 映射中:text类型字段需要分词;keyword类型无需分词,精确查询;数值类型尽量选范围小的
- ES更新文档的顺序是:先检索到文档、将原来的文档标记为删除、创建新文档、删除旧文档,创建新文档就会重建索引。
用户认证
单点登录技术
- 需要将认证系统独立抽取出来,用户访问其他系统时,借助认证系统来认证。
- 使用Redis管理用户身份。
Oauth2
认证流程
Oauth2 是一个用户资源授权的协议。
- 客户端(浏览器)申请认证
- 资源持有者(用户)授权
- 客户端获得授权码后请求授权服务器(微信认证)颁发令牌
- 客户端获得令牌后可以访问资源服务(资源拥有者的资源)
授权模式
- 授权码模式:通过授权码申请令牌(第三方登录)
- 密码模式:直接通过用户名和密码申请令牌(用户密码登录)
项目中的用户认证架构
Spring Security + Oauth2
原理:
认证:是否有资格访问系统
- 用户在前端界面请求认证服务完成认证。
- 认证服务下发用户身份令牌,拥有身份令牌表示身份合法。
- 用户携带令牌请求资源服务,请求资源服务必先经过网关。
- 网关校验用户身份令牌的合法,不合法表示用户没有登录,如果合法则放行继续访问。
进入微服务:授权访问,校验是否有权限访问某些资源
- 资源服务获取令牌,校验令牌完成授权。
- 资源服务完成授权则响应资源信息。
项目中执行流程:
本项目采用 Spring security + Oauth2完成用户认证及用户授权。认证授权流程如下:
1、用户请求认证服务完成身份认证。
- 认证服务通过远程调用用户中心服务来查询用户信息
2、认证服务通过Spring Security下发用户身份令牌和JWT令牌,拥有身份令牌表示身份合法,Jwt令牌用于完成授权。 - Spring Security是通过一个申请令牌的url
/oauth/token
,调用estTemplate的exchange方法申请令牌 - 用户令牌保存到cookie,Jwt令牌保存到redis
3、用户携带jwt令牌请求资源服务。
4、网关校验用户身份令牌的合法,不合法表示用户没有登录,如果合法则放行继续访问。
5、资源服务获取jwt令牌,根据jwt令牌完成授权。
6、用户退出,请求认证服务,清除redis中的令牌,并且删除cookie中的令牌
使用redis存储用户的身份令牌有以下作用:
1、实现用户退出注销功能,服务端清除令牌后,即使客户端请求携带token也是无效的。
2、由于jwt令牌过长,不宜存储在cookie中,所以将jwt令牌存储在redis,由客户端请求服务端获取并在客户端存储。
申请令牌通过Spring Security实现
流程如下:
- 前端请求认证
- 认证服务通过 Spring Security 申请令牌(密码方式)
- Spring Security 请求用户账号和密码(UserDetail)
- 如果请求到的用户存在,比对密码。密码符合,发放令牌
令牌
- 校验令牌:用户首次登录时通过密码和用户名生成一个新的令牌
- 刷新令牌:当前令牌快过期时,自动使用刷新令牌,避免用户再次登录
JWT 令牌
资源服务获每一次取到传统令牌后都需要通过认证服务校验令牌的合法性,性能低下;由于JWT令牌本身就携带了用户相关信息(如用户的权限),使得资源服务可以自行完成身份校验,不用借助认证服务。(具体原理看结构)
JWT令牌的优点:
1、jwt基于json,非常方便解析。
2、可以在令牌中自定义丰富的内容,易扩展。
3、通过非对称加密算法及数字签名技术,JWT防止篡改,安全性高。
4、资源服务使用JWT可不依赖认证服务即可完成授权。
但是令牌较长,占储存空间大。
结构
- Header:包括令牌的类型(即JWT)及使用的哈希算法(如HMAC SHA256或RSA)
- Payload:负载,内容也是一个json对象,它是存放有效信息的地方,它可以存放jwt提供的现成字段,比如:iss(签发者),exp(过期时间戳), sub(面向的用户)等,也可自定义字段。
- Signature:用于防止jwt内容被篡改。
这个部分使用base64url将前两部分进行编码,编码后使用点(.)连接组成字符串,最后使用header中声明的签名算法进行签名。 - 如果第二部分被篡改,使用约定的算法进行签名后,会发现和本身的签名不同,就会造成认证失败。第三部分签名是无法篡改的,因为包括一个签名所使用的的秘钥,篡改者是无法知道的。
Zuul 网关
相当于一个拦截器,只有用户校验成功,才能对微服务进行访问。
Zuul与Nginx在实际项目中需要配合使用,Nginx的作用是反向代理、负载均衡;Zuul的作用是保障微服务的安全访问,拦截微服务请求,校验合法性及负载均衡。
- 启动类上要标识@EnableZuulProxy
- 在application.yml中配置各个微服务路由
- 通过过滤器ZuulFilter实现身份校验,需要连接redis获取令牌信息
注意:网关校验的是身份令牌,微服务才通过jwt令牌进行用户授权
用户、微服务授权
微服务通过解析jwt用户信息,可以根据用户信息进行基于方法的授权或者细粒度授权。
方法授权
- 生成Jwt令牌时在令牌中写入用户所拥有的权限
- 在资源服务方法上添加注解PreAuthorize,并指定此方法所需要的权限
@PreAuthorize("hasAuthority('course_find_list')")
- 当请求有权限的方法时正常访问
- 当请求没有权限的方法时则拒绝访问
细粒度授权
- 不同的用户所拥有的操作权限相同,但是能够操作的数据范围是不一样的。
- 一个例子:用户A和用户B都是教学机构,他们都拥有“我的课程”权限,但是两个用户所查询到的数据是不一样的。
- 本项目有哪些细粒度授权?
- 我的课程,教学机构只允许查询本教学机构下的课程信息。
- 我的选课,学生只允许查询自己所选课。
- 如何实现细粒度授权?
- 细粒度授权涉及到不同的业务逻辑,通常在service层实现,根据不同的用户进行校验,根据不同的参数查询不同的数据或操作不同的数据。
微服务间的认证
微服务之间进行调用,也需要携带jwt令牌,才能进行授权使用。
比如课程管理、CMS都需要进行授权才能访问。
解决:使用Feign拦截器实现远程调用携带JWT。
- 在Common工程定义拦截器
- 在微服务启动类中定义Feign拦截器bean
本作品采用《CC 协议》,转载必须注明作者和本文链接