详解SpringCloud服务认证(JWT)

 - 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" ]
}
  1. iss: 该JWT的签发者,是否使用是可选的;
  2. sub: 该JWT所面向的用户,是否使用是可选的;
  3. aud: 接收该JWT的一方,是否使用是可选的;
  4. exp(expires): 什么时候过期,这里是一个Unix时间戳,是否使用是可选的;
  5. iat(issued at): 在什么时候签发的(UNIX时间),是否使用是可选的;
  6. nbf (Not Before):如果当前时间在nbf里的时间之前,则Token不被接受;一般都会留一些余地,比如几分钟;,是否使用是可选的;

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

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

- 认证

- 登陆认证

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

- 请求认证

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

无效Token

有效Token

- JWT的缺点

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

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

 @Autowired
 public 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 # 分钟 1440
 refresh-exp-time: 30 # 分钟 2880
 issuer: http://blog.battcn.com
 signing-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
 @Override
 public AuthenticationManager authenticationManagerBean() throws Exception {
 return super.authenticationManagerBean();
 }

 @Override
 protected void configure(AuthenticationManagerBuilder auth) {
 auth.authenticationProvider(loginAuthenticationProvider);
 auth.authenticationProvider(tokenAuthenticationProvider);
 }

 @Override
 protected 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):http://xiazai.jb51.net/201801/yuanma/battcn-cloud_jb51.rar

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

您可能感兴趣的文章:

  • spring cloud 之 Feign 使用HTTP请求远程服务的实现方法
  • 最简单的Spring Cloud教程第一篇:服务的注册与发现(Eureka)
  • 详解利用SpringCloud搭建一个最简单的微服务框架
  • 浅谈Spring Cloud中的API网关服务Zuul
  • 详解Spring Cloud Zuul 服务网关
  • 浅谈SpringCloud实现简单的微服务架构
  • spring cloud将spring boot服务注册到Eureka Server上的方法
  • 详解SpringCloud微服务架构之Hystrix断路器
  • spring-cloud入门之eureka-server(服务发现)
(0)

相关推荐

  • 详解Spring Cloud Zuul 服务网关

    有了Eureka服务注册发现.Hystrix断路器.Ribbon服务调用负载均衡,以及spring cloud config 集群配置中心,似乎一个微服务框架已五脏俱全,last but not least,一个服务网关却不可或缺. Spring Cloud Zuul路由是微服务架构的不可或缺的一部分,提供动态路由,监控,弹性,安全等的边缘服务.Zuul是Netflix出品的一个基于JVM路由和服务端的负载均衡器. Zuul介绍 在整个Spring Cloud微服务框架里,Zuul扮演着"智能网

  • spring cloud 之 Feign 使用HTTP请求远程服务的实现方法

    一.Feign 简介 在spring Cloud Netflix栈中,各个微服务都是以HTTP接口的形式暴露自身服务的,因此在调用远程服务时就必须使用HTTP客户端.我们可以使用JDK原生的URLConnection.Apache的Http Client.Netty的异步HTTP Client, Spring的RestTemplate.但是,用起来最方便.最优雅的还是要属Feign了. Feign是一种声明式.模板化的HTTP客户端.在Spring Cloud中使用Feign, 我们可以做到使用

  • 浅谈Spring Cloud中的API网关服务Zuul

    到目前为止,我们Spring Cloud中的内容已经介绍了很多了,Ribbon.Hystrix.Feign这些知识点大家都耳熟能详了,我们在前文也提到过微服务就是把一个大的项目拆分成很多小的独立模块,然后通过服务治理让这些独立的模块配合工作等.那么大家来想这样两个问题:1.如果我的微服务中有很多个独立服务都要对外提供服务,那么对于开发人员或者运维人员来说,他要如何去管理这些接口?特别是当项目非常大非常庞杂的情况下要如何管理?2.权限管理也是一个老生常谈的问题,在微服务中,一个独立的系统被拆分成很

  • 浅谈SpringCloud实现简单的微服务架构

    Spring Cloud是一系列框架的有序集合.它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册.配置中心.消息总线.负载均衡.断路器.数据监控等,都可以用Spring Boot的开发风格做到一键启动和部署.Spring并没有重复制造轮子,它只是将目前各家公司开发的比较成熟.经得起实际考验的服务框架组合起来,通过Spring Boot风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂.易部署和易维护的分布式系统开发工具包. 接下

  • 详解SpringCloud微服务架构之Hystrix断路器

    一:什么是Hystrix 在分布式环境中,许多服务依赖项中的一些将不可避免地失败.Hystrix是一个库,通过添加延迟容差和容错逻辑来帮助您控制这些分布式服务之间的交互.Hystrix通过隔离服务之间的访问点,停止其间的级联故障以及提供回退选项,从而提高系统的整体弹性. Hystrix旨在执行以下操作 1:对通过第三方客户端库访问(通常通过网络)的依赖关系提供保护并控制延迟和故障. 2:隔离复杂分布式系统中的级联故障. 3:快速发现故障,尽快恢复. 4:回退,尽可能优雅地降级. 5:启用近实时监

  • 最简单的Spring Cloud教程第一篇:服务的注册与发现(Eureka)

    前言 本文主要给大家介绍关于Spring Cloud服务注册与发现(Eureka)的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍: 一.spring cloud简介 spring cloud 为开发人员提供了快速构建分布式系统的一些工具,包括配置管理.服务发现.断路器.路由.微代理.事件总线.全局锁.决策竞选.分布式会话等等.它运行环境简单,可以在开发人员的电脑上跑.另外说明spring cloud是基于springboot的,所以需要开发中对springboot有一定

  • 详解利用SpringCloud搭建一个最简单的微服务框架

    Spring Cloud是一个基于Spring Boot实现的云应用开发工具,它为基于JVM的云应用开发中的配置管理.服务发现.断路器.智能路由.微代理.控制总线.全局锁.决策竞选.分布式会话和集群状态管理等操作提供了一种简单的开发方式. Spring Cloud包含了多个子项目(针对分布式系统中涉及的多个不同开源产品),比如:Spring Cloud Config.Spring Cloud Netflix.Spring Cloud CloudFoundry.Spring Cloud AWS.S

  • spring cloud将spring boot服务注册到Eureka Server上的方法

    开篇: 我们将前面的springboot整合H2内存数据库,实现单元测试与数据库无关性提供的Restful服务注册到spring cloud的Eureka Server上. 一.引入Eureka的Client </dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</ar

  • spring-cloud入门之eureka-server(服务发现)

    前言 Eureka是一个服务发现和注册框架,细的来说,我们可以分为eureka-server(服务发现)和eureka-client(服务注册)两个,本次我们对eureka-server(服务发现)做一个项目搭建,作为spring-cloud的开篇. 开源地址:https://github.com/bigbeef 项目结构 maven结构大家应该都清楚(不清楚的需要补一补,百度关于maven的文章不计其数),下面我们来看一看这些关键文件的配置 代码编写 cppba-spring-cloud >

  • 详解SpringCloud服务认证(JWT)

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

  • 详解SpringCloud微服务之Rest

    目录 一.什么是RestTemplate? 二.四种请求方式 2.1 GET请求 2.2 POST请求 2.3 PUT请求 2.4 DELETE请求 一.什么是RestTemplate? RestTemplate 是一个HTTP客户端,在Spring Cloud的服务调用方使用它我们可以方便的调用HTTP接口,支持GET.POST.PUT.DELETE等方法. 二.四种请求方式 首先注入Bean对象 @Configuration public class MyConfig { @Bean pub

  • 详解微服务架构及其演进史

    目录 1 传统单体系统介绍 1.1 单体系统的问题 1.2 单体系统的优点 1.3 单体服务到微服务的发展过程 2 关于微服务 2.1 单一职责 2.2 轻量级通信 2.3 独立性 2.4 进程隔离 2.5 混合技术栈和混合部署方式 2.6 简化治理 2.7 安全可靠,可维护. 3 微服务演进史 3.1 第一阶:简单服务通信模块 3.2 第二阶:原始通信时代 3.3 第三阶:TCP时代 3.4 第四阶:第一代微服务(Spring Cloud/RPC) 3.5 第五阶:第二代微服务 3.6 第六阶

  • 详解SpringCloud新一代网关Gateway

    一.概述简介 1.1.简介 SpringCloud Gateway作为Spring Cloud生态系统中的网关,目标是替代Zuul,在Spring Cloud 2.0以上版本中,没有对新版本的Zuul 2.0以上最新高性能版本进行集成,仍然还是使用的Zuul 1.x非Reactor模式的老版本.而为了提升网关的性能,SpringCloud Gateway是基于WebFlux框架实现的,而WebFlux框架底层则使用了高性能的Reactor模式通信框架Netty. Spring Cloud Gat

  • 详解SpringCloud的负载均衡

    目录 一.什么是负载均衡 二.负载均衡的简单分类 三.为什么需要做负载均衡 四.springCloud如何开启负载均衡 五.IRule 1.RandomRule:表示随机策略,它将从服务清单中随机选择一个服务: 2.ClientConfigEnabledRoundRobinRule:ClientConfigEnabledRoundRobinRule并没有实现什么特殊的处理逻辑,但是他的子类可以实现一些高级策略, 当一些本身的策略无法实现某些需求的时候,它也可以做为父类帮助实现某些策略,一般情况下

  • Django 自定义权限管理系统详解(通过中间件认证)

    1. 创建工程文件, 修改setting.py文件 django-admin.py startproject project_name 特别是在 windows 上,如果报错,尝试用 django-admin 代替 django-admin.py 试试 setting.py 最终的配置文件 import os import sys # Build paths inside the project like this: os.path.join(BASE_DIR, ...) BASE_DIR =

  • 详解shrio的认证(登录)过程

    shrio是一个比较轻量级的安全框架,主要的作用是在后端承担认证和授权的工作.今天就讲一下shrio进行认证的一个过程. 首先先介绍一下在认证过程中的几个关键的对象: Subject:主体 访问系统的用户,主体可以是用户.程序等,进行认证的都称为主体: Principal:身份信息 是主体(subject)进行身份认证的标识,标识必须具有唯一性,如用户名.手机号.邮箱地址等,一个主体可以有多个身份,但是必须有一个主身份(Primary Principal). credential:凭证信息 是只

  • 详解vue身份认证管理和租户管理

    概述 功能模块的开发往往是最容易的,但是要处理好每个细节就不容易了.就拿这里的身份认证管理模块来说,看似很简单,因为后端接口都是ABP模板里现成的,前端部分无非就是写界面,调接口,绑数据:但是看一下ABP Angular版本的代码,就会发现他其实是有很多细节方面的处理的. 回到vue,因为前端部分的代码文件太多,下面只列出一些需要注意的细节,其他的像vue组件.表格.表单.数据绑定.接口请求之类的其实都差不多就不说了. 按钮级权限 前面章节中实现了菜单权限的控制,按钮权限的道理也是一样的.判断a

  • 详解Laravel服务容器的优势

    概述 laravel服务容器就像一个高度自动化的工厂,你需要的东西,定制好模型,使用特定接口来制造. 因为使用了服务容器,laravel中大部分对象实例化的方式是这样的: $obj1 = $container->make('class1', 'class2'); $obj2 = $container->make('class3', 'class4'); 但是在没有使用服务容器的情况下,以下这种方式同样可以做到: $obj1 = new class1(new class2()); $obj2 =

  • 详解springcloud之服务注册与发现

    本次分享的是关于springcloud服务注册与发现的内容,将通过分别搭建服务中心,服务注册,服务发现来说明:现在北京这边很多创业公司都开始往springcloud靠了,可能是由于文档和组件比较丰富的原因吧,毕竟是一款目前来说比较完善的微服务架构:本次分享希望能给大家带来好的帮助: Eureka服务中心 Provider注册服务 Consumer发现服务 Eureka服务中心高可用 Eureka服务中心 就我现在了解到并且用的比较多的注册中心有zookeeper和Eureka,我的上上篇文章分享

随机推荐