上一篇已经讲了微服务组件中的 路由网关(Zuul),但是未介绍服务认证相关,本章主要讲解基于Spring SecurityJJWT 实现 JWT(JSON Web Token)为接口做授权处理…

- JWT

JWT(JSON Web Token), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519).该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。

- JWT与其它的区别

通常情况下,把API直接暴露出去是风险很大的,不说别的,直接被机器攻击就喝一壶的。那么一般来说,对API要划分出一定的权限级别,然后做一个用户的鉴权,依据鉴权结果给予用户开放对应的API。目前,比较主流的方案有几种:

OAuth

OAuth(开放授权)是一个开放的授权标准,允许用户让第三方应用访问该用户在某一服务上存储的私密的资源(如照片,视频),而无需将用户名和密码提供给第三方应用。

OAuth 允许用户提供一个令牌,而不是用户名和密码来访问他们存放在特定服务提供者的数据。每一个令牌授权一个特定的第三方系统(例如,视频编辑网站)在特定的时段(例如,接下来的2小时内)内访问特定的资源(例如仅仅是某一相册中的视频)。这样,OAuth让用户可以授权第三方网站访问他们存储在另外服务提供者的某些特定信息,而非所有内容

Cookie/Session Auth

Cookie认证机制就是为一次请求认证在服务端创建一个Session对象,同时在客户端的浏览器端创建了一个Cookie对象;通过客户端带上来Cookie对象来与服务器端的session对象匹配来实现状态管理的。默认的,当我们关闭浏览器的时候,cookie会被删除。但可以通过修改cookie 的expire time使cookie在一定时间内有效,基于session方式认证势必会对服务器造成一定的压力(内存存储),不易于扩展(需要处理分布式session),跨站请求伪造的攻击(CSRF)

- JWT的优点

1.相比于session,它无需保存在服务器,不占用服务器内存开销。

2.无状态、可拓展性强:比如有3台机器(A、B、C)组成服务器集群,若session存在机器A上,session只能保存在其中一台服务器,此时你便不能访问机器B、C,因为B、C上没有存放该Session,而使用token就能够验证用户请求合法性,并且我再加几台机器也没事,所以可拓展性好就是这个意思。

3.前后端分离,支持跨域访问。

- JWT的组成

{ "iss": "JWT Builder", "iat": 1416797419, "exp": 1448333419, "aud": "www.battcn.com", "sub": "1837307557@qq.com", "GivenName": "Levin", "Surname": "Levin", "Email": "1837307557@qq.com", "Role": [ "ADMIN", "MEMBER" ]
}
  • iss: 该JWT的签发者,是否使用是可选的;
  • sub: 该JWT所面向的用户,是否使用是可选的;
  • aud: 接收该JWT的一方,是否使用是可选的;
  • exp(expires): 什么时候过期,这里是一个Unix时间戳,是否使用是可选的;
  • iat(issued at): 在什么时候签发的(UNIX时间),是否使用是可选的;
  • nbf (Not Before):如果当前时间在nbf里的时间之前,则Token不被接受;一般都会留一些余地,比如几分钟;,是否使用是可选的;

一个JWT实际上就是一个字符串,它由三部分组成,头部、载荷、签名(上图依次排序)

JWT Token生成器:https://jwt.io/

- 认证

- 登陆认证

  • 客户端发送 POST 请求到服务器,提交登录处理的Controller层
  • 调用认证服务进行用户名密码认证,如果认证通过,返回完整的用户信息及对应权限信息
  • 利用 JJWT 对用户、权限信息、秘钥构建Token
  • 返回构建好的Token

- 请求认证

  • 客户端向服务器请求,服务端读取请求头信息(request.header)获取Token
  • 如果找到Token信息,则根据配置文件中的签名加密秘钥,调用JJWT Lib对Token信息进行解密和解码;
  • 完成解码并验证签名通过后,对Token中的exp、nbf、aud等信息进行验证;
  • 全部通过后,根据获取的用户的角色权限信息,进行对请求的资源的权限逻辑判断;
  • 如果权限逻辑判断通过则通过Response对象返回;否则则返回HTTP 401;

无效Token

有效Token

- JWT的缺点

有优点就会有缺点,是否适用应该考虑清楚,而不是技术跟风

  • token过大容易占用更多的空间
  • token中不应该存储敏感信息
  • JWT不是 session ,勿将token当session
  • 无法作废已颁布的令牌,因为所有的认证信息都在JWT中,由于在服务端没有状态,即使你知道了某个JWT被盗取了,你也没有办法将其作废。在JWT过期之前(你绝对应该设置过期时间),你无能为力。
  • 类似缓存,由于无法作废已颁布的令牌,在其过期前,你只能忍受”过期”的数据(自己放出去的token,含着泪也要用到底)。

- 代码(片段)

TokenProperties 与 application.yml资源的key映射,方便使用

@Configuration
@ConfigurationProperties(prefix = "battcn.security.token")
public class TokenProperties {/*** {@link com.battcn.security.model.token.Token} token的过期时间*/private Integer expirationTime;/*** 发行人*/private String issuer;/*** 使用的签名KEY {@link com.battcn.security.model.token.Token}.*/private String signingKey;/*** {@link com.battcn.security.model.token.Token} 刷新过期时间*/private Integer refreshExpTime;// get set ...
}

Token生成的类

@Component
public class TokenFactory {private final TokenProperties properties;@Autowiredpublic TokenFactory(TokenProperties properties) {this.properties = properties;}/*** 利用JJWT 生成 Token* @param context* @return*/public AccessToken createAccessToken(UserContext context) {Optional.ofNullable(context.getUsername()).orElseThrow(()-> new IllegalArgumentException("Cannot create Token without username"));Optional.ofNullable(context.getAuthorities()).orElseThrow(()-> new IllegalArgumentException("User doesn't have any privileges"));Claims claims = Jwts.claims().setSubject(context.getUsername());claims.put("scopes", context.getAuthorities().stream().map(Object::toString).collect(toList()));LocalDateTime currentTime = LocalDateTime.now();String token = Jwts.builder().setClaims(claims).setIssuer(properties.getIssuer()).setIssuedAt(Date.from(currentTime.atZone(ZoneId.systemDefault()).toInstant())).setExpiration(Date.from(currentTime.plusMinutes(properties.getExpirationTime()).atZone(ZoneId.systemDefault()).toInstant())).signWith(SignatureAlgorithm.HS512, properties.getSigningKey()).compact();return new AccessToken(token, claims);}/*** 生成 刷新 RefreshToken* @param userContext* @return*/public Token createRefreshToken(UserContext userContext) {if (StringUtils.isBlank(userContext.getUsername())) {throw new IllegalArgumentException("Cannot create Token without username");}LocalDateTime currentTime = LocalDateTime.now();Claims claims = Jwts.claims().setSubject(userContext.getUsername());claims.put("scopes", Arrays.asList(Scopes.REFRESH_TOKEN.authority()));String token = Jwts.builder().setClaims(claims).setIssuer(properties.getIssuer()).setId(UUID.randomUUID().toString()).setIssuedAt(Date.from(currentTime.atZone(ZoneId.systemDefault()).toInstant())).setExpiration(Date.from(currentTime.plusMinutes(properties.getRefreshExpTime()).atZone(ZoneId.systemDefault()).toInstant())).signWith(SignatureAlgorithm.HS512, properties.getSigningKey()).compact();return new AccessToken(token, claims);}
}

配置文件,含token过期时间,秘钥,可自行扩展

battcn:security:token:expiration-time: 10 # 分钟 1440refresh-exp-time: 30 # 分钟 2880issuer: http://blog.battcn.comsigning-key: battcn

WebSecurityConfig 是 Spring Security 关键配置,在Securrty中基本上可以通过定义过滤器去实现我们想要的功能.

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {public static final String TOKEN_HEADER_PARAM = "X-Authorization";public static final String FORM_BASED_LOGIN_ENTRY_POINT = "/api/auth/login";public static final String TOKEN_BASED_AUTH_ENTRY_POINT = "/api/**";public static final String MANAGE_TOKEN_BASED_AUTH_ENTRY_POINT = "/manage/**";public static final String TOKEN_REFRESH_ENTRY_POINT = "/api/auth/token";@Autowired private RestAuthenticationEntryPoint authenticationEntryPoint;@Autowired private AuthenticationSuccessHandler successHandler;@Autowired private AuthenticationFailureHandler failureHandler;@Autowired private LoginAuthenticationProvider loginAuthenticationProvider;@Autowired private TokenAuthenticationProvider tokenAuthenticationProvider;@Autowired private TokenExtractor tokenExtractor;@Autowired private AuthenticationManager authenticationManager;protected LoginProcessingFilter buildLoginProcessingFilter() throws Exception {LoginProcessingFilter filter = new LoginProcessingFilter(FORM_BASED_LOGIN_ENTRY_POINT, successHandler, failureHandler);filter.setAuthenticationManager(this.authenticationManager);return filter;}protected TokenAuthenticationProcessingFilter buildTokenAuthenticationProcessingFilter() throws Exception {List<String> list = Lists.newArrayList(TOKEN_BASED_AUTH_ENTRY_POINT,MANAGE_TOKEN_BASED_AUTH_ENTRY_POINT);SkipPathRequestMatcher matcher = new SkipPathRequestMatcher(list);TokenAuthenticationProcessingFilter filter = new TokenAuthenticationProcessingFilter(failureHandler, tokenExtractor, matcher);filter.setAuthenticationManager(this.authenticationManager);return filter;}@Bean@Overridepublic AuthenticationManager authenticationManagerBean() throws Exception {return super.authenticationManagerBean();}@Overrideprotected void configure(AuthenticationManagerBuilder auth) {auth.authenticationProvider(loginAuthenticationProvider);auth.authenticationProvider(tokenAuthenticationProvider);}@Overrideprotected void configure(HttpSecurity http) throws Exception {http.csrf().disable() // 因为使用的是JWT,因此这里可以关闭csrf了.exceptionHandling().authenticationEntryPoint(this.authenticationEntryPoint).and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().authorizeRequests().antMatchers(FORM_BASED_LOGIN_ENTRY_POINT).permitAll() // Login end-point.antMatchers(TOKEN_REFRESH_ENTRY_POINT).permitAll() // Token refresh end-point.and().authorizeRequests().antMatchers(TOKEN_BASED_AUTH_ENTRY_POINT).authenticated() // Protected API End-points.antMatchers(MANAGE_TOKEN_BASED_AUTH_ENTRY_POINT).hasAnyRole(RoleEnum.ADMIN.name()).and().addFilterBefore(buildLoginProcessingFilter(), UsernamePasswordAuthenticationFilter.class).addFilterBefore(buildTokenAuthenticationProcessingFilter(), UsernamePasswordAuthenticationFilter.class);}
}

- 说点什么

由于JWT代码做了简单封装,包含内容较多,所以文章里只贴主要片段,需要完整代码可以直接从下面GIT获取

本章代码(battcn-jwt-service):https://git.oschina.net/battcn/battcn-cloud/tree/master/battcn-cloud-jwt/battcn-jwt-service

如有问题请及时与我联系

  • 个人QQ:1837307557
  • Spring Cloud中国社区①:415028731
  • Spring For All 社区⑤:157525002

一起来学SpringCloud之 - 服务认证(JWT)相关推荐

  1. springcloud 微服务鉴权_springcloud 微服务权限校验JWT模式获取 token 实战(十二)...

    springcloud 微服务权限校验JWT模式获取 token 实战(十二) springcloud 微服务权限校验JWT模式获取 token 实战(十二) JWT:json web token 是 ...

  2. Spring Cloud OAuth2 JWT 微服务认证服务器得构建

    文章目录 Spring Cloud OAuth2 JWT 微服务认证服务器得构建 前言 认证服务得搭建 `AuthorizationServer` `WebSecurityConfig` `Autho ...

  3. 从零开始超详细的Spring Security OAuth2.0实现分布式系统授权(注册中心+网关+认证授权服务(JWT令牌验证)+资源调用服务)

    文章目录 一.OAuth2.0 1.介绍 2.例子 3.执行流程 二.Spring Cloud Security OAuth2 1.环境介绍 2.认证流程 三.整合分布式项目 1.技术方案 2.项目结 ...

  4. 从0到1学SpringCloud——08 通过fegin实现微服务之间请求调用

    目录 一.前言 1.关于Fegin 2.注意事项 3.POM依赖 二.编码实现 1.启动类 2.创建openfeign接口 3.Controller代码 4.回调工厂 三.文件配置 1.Feign接口 ...

  5. SpringCloud学习一(回顾之前学的微服务知识点、springcloud入门概述、服务提供者和消费者)

    一.回顾之前,如何学习springcloud 回顾之前的知识? JavaSE 数据库 前端 Servlet Http Mybatis Spring SpringMVC SpringBoot Dubbo ...

  6. SpringCloud 微服务架构,适合接私活(附源码)

    欢迎关注方志朋的博客,回复"666"获面试宝典 今天给大家推荐一个牛逼的接私活项目,SpringCloud微服务架构项目! 一个由商业级项目升级优化而来的微服务架构,采用Sprin ...

  7. 快速搭建 SpringCloud 微服务开发环境的脚手架

    快速搭建 SpringCloud 微服务开发环境的脚手架 本文作者:HelloGitHub-秦人 本文适合有 SpringBoot 和 SpringCloud 基础知识的人群,跟着本文可使用和快速搭建 ...

  8. 介绍6款热门的SpringCloud微服务开源项目,总有适合你的!

    今天介绍六款比较热门的SpringCloud微服务项目,感兴趣的可以clone下来研究一下,相信对你学习微服务架构很有帮助. 一.Cloud-Platform 介绍 Cloud-Platform是国内 ...

  9. SpringCloud微服务,euraka、feign、hystrix组件学习

    SpringCloud 1 eureka 1.1 eureka基本概念 eureka主要包含两个组件:Eureka Server 和 Eureka Client. eureka server(注册中心 ...

  10. SpringCloud 微服务架构开源项目,适合接私活、毕业设计(附源码)

    今天给大家推荐一个牛逼的接私活项目,SpringCloud微服务架构项目! 一个由商业级项目升级优化而来的微服务架构,采用SpringBoot 2.5 .SpringCloud 等核心技术构建,提供基 ...

最新文章

  1. Git——Git基本教程
  2. JS的indexOf
  3. [51nod] 1301 集合异或和
  4. android应用退出后广播无效,关闭应用程序后,保持广播接收器运行
  5. C++ 常用排序算法
  6. 【转】分布式事务的常见解决方案
  7. access 根据id删除数据_小程序云开发之数据库自动备份丨云开发101
  8. 【Python】提升Python程序性能的好习惯2
  9. zabbix api java_zabbix的Java API(一)
  10. Js toString()方法笔记
  11. 适合千万数据查询分页操作的一个通用存储过程
  12. Eclipse 中 工程 引用 其他 工程的 配置方法。
  13. 评分预测会不会大于满分5.0的情况?
  14. 求多个数最小公倍数的一种变换算法
  15. IDEA插件系列-玩转JSON与实体类互相转换
  16. linux麒麟认证,【麒麟在线讲堂】优麒麟生物特征认证系统-03驱动开发
  17. matlab help函数用法,MATLAB函数用法
  18. 第0课:郭盛华课程_零基础学Visual Basic编程语言
  19. 3dmax导出fbx模型到unity
  20. Unity Shader------Specular(高光反射)计算

热门文章

  1. 洛谷 【入门4】数组 P1427 小鱼的数字游戏
  2. access数据库剔除重复项_使用Access数据库的站长看过来——如何自动去掉数据库中的重复文章...
  3. 腾讯云PCDN:从P2P到万物互联服务框架
  4. RTC风向标:11月最值得关注的26个热点
  5. Python解析器安装后测试打开微软商店
  6. 白话空间统计二十三回归分析番外:残差可视化
  7. 2022智源大会议程公开 | 视觉模型论坛
  8. 证券交易所的运作系统
  9. 网卡,交换机和路由器
  10. linux无线网卡驱动编写,博通无线网卡驱动linux版