上一篇已经讲了微服务组件中的 路由网关(Zuul),但是未介绍服务认证相关,本章主要讲解基于Spring Security 与 JJWT 实现 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的组成

1
2
3
4
5
6
7
8
9
10
{ "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实际上就是一个字符串,它由三部分组成,头部、载荷、签名(上图依次排序)

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

有效Token

有效Token请求

- JWT的缺点

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

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

- 代码(片段)

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@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生成的类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
@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过期时间,秘钥,可自行扩展

1
2
3
4
5
6
7
battcn:
security:
token:
expiration-time: 10 # 分钟 1440
refresh-exp-time: 30 # 分钟 2880
issuer: http://blog.battcn.com
signing-key: battcn

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
@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)的更多相关文章

  1. 服务认证暴力破解工具Crowbar

    服务认证暴力破解工具Crowbar   Crowbar是Kali Linux新增的一款服务认证暴力破解工具.该工具支持OpenVPN.RDP.SSH和VNC服务.该工具具备常见的暴力破解功能,如主机字 ...

  2. SpringCloud学习笔记(5)----Spring Cloud Netflix之Eureka的服务认证和集群

    1. Eureka服务认证 1. 引入依赖 <dependency> <groupId>org.springframework.boot</groupId> < ...

  3. asp.net core系列 60 Ocelot 构建服务认证示例

    一.概述 在Ocelot中,为了保护下游api资源,用户访问时需要进行认证鉴权,这需要在Ocelot 网关中添加认证服务.添加认证后,ReRoutes路由会进行身份验证,并使用Ocelot的基于声明的 ...

  4. 二.3.token认证,jwt认证,前端框架

    一.token: 铺垫: 之前用的是通过最基本的用户名密码登录我的运维平台http://127.0.0.1:8000/---这种用的是form表单,但是这种对于前后端分离的不适合.前后端分离,应该通过 ...

  5. H3C交换机telnet服务认证模式配置

    以H3C交换机为例,介绍telnet服务的三种认证方式配置(none无需认证,password密码认证,scheme账户+密码认证) None认证模式配置步骤:[H3C]telnet server e ...

  6. 服务安全-JWT(JSON Web Tokens):百科

    ylbtech-服务安全-JWT(JSON Web Tokens):百科 JSON Web Tokens是一种开放的行业标准 RFC 7519方法,用于在双方之间安全地表示索赔. JWT.IO允许您解 ...

  7. (9)学习笔记 ) ASP.NET CORE微服务 Micro-Service ---- JWT算法

    一. JWT 简介 内部 Restful 接口可以“我家大门常打开”,但是如果要给 app 等使用的接口,则需要做权限校验,不能谁都随便调用. Restful 接口不是 web 网站,App 中很难直 ...

  8. 苹果登录服务端JWT算法验证-PHP

    验证参数 可用的验证参数有 userID.authorizationCode.identityToken,需要iOS客户端传过来 验证方式 苹果登录验证可以选择两种验证方式 具体可参考这篇文章 htt ...

  9. 微服务统一登陆认证怎么做?JWT ?

    无状态登录原理 1.1.什么是有状态? 有状态服务,即服务端需要记录每次会话的客户端信息,从而识别客户端身份,根据用户身份进行请求的处理,典型的设计如tomcat中的session. 例如登录:用户登 ...

随机推荐

  1. Java实现 蓝桥杯VIP 算法提高 计算时间

    算法提高 计算时间 时间限制:1.0s 内存限制:512.0MB 问题描述 给定一个t,将t秒转化为HH:MM:SS的形式,表示HH小时MM分钟SS秒.HH,MM,SS均是两位数,如果小于10用0补到 ...

  2. 第二届蓝桥杯C++B组国(决)赛真题

    以下代码仅供参考,解答部分来自网友,对于正确性不能保证,如有错误欢迎评论 四方定理. 数论中有著名的四方定理:所有自然数至多只要用四个数的平方和就可以表示. 我们可以通过计算机验证其在有限范围的正确性 ...

  3. java实现拍7游戏

    ** 拍7游戏** 许多人都曾经玩过"拍七"游戏.规则是:大家依次从1开始顺序数数,数到含有7或7的倍数的要拍手或其它规定的方式表示越过(比如:7,14,17等都不能数出),下一人 ...

  4. java实现土地测量

    ** 土地测量** 造成高房价的原因有许多,比如土地出让价格.既然地价高,土地的面积必须仔细计算.遗憾的是,有些地块的形状不规则,比如是如图[1.jpg]中所示的五边形. 一般需要把它划分为多个三角形 ...

  5. java实现第八届蓝桥杯树型显示

    树型显示 题目描述 对于分类结构可以用树形来形象地表示.比如:文件系统就是典型的例子. 树中的结点具有父子关系.我们在显示的时候,把子项向右缩进(用空格,不是tab),并添加必要的连接线,以使其层次关 ...

  6. unittest单元测试框架入门及应用

    一.简介 unittest是Python单元测试框架.unittest它支持自动化测试,在测试中使用setup(初始化)和shutdown(关闭销毁)操作,组织测试 用例为套件(批量运行),以及把测试 ...

  7. 【大厂面试05期】说一说你对MySQL中锁的了解?

    这是我总结的一个表格,是本文中涉及到的锁(因为篇幅有限就没有包括自增锁) 加锁范围 名称 用法 数据库级 全局读锁 执行Flush tables with read lock命令各整个库接加一个读锁, ...

  8. [原创][开源]SunnyUI.Net, C# .Net WinForm开源控件库、工具类库、扩展类库、多页面开发框架

    SunnyUI.Net, 基于 C# .Net WinForm 开源控件库.工具类库.扩展类库.多页面开发框架 Blog: https://www.cnblogs.com/yhuse Gitee: h ...

  9. 超强教程!在树莓派上构建多节点K8S集群!

    在很长一段时间里,我对于在树莓派上搭建Kubernetes集群极为感兴趣.在网络上找到一些教程并且跟着实操,我已经能够将Kubernetes安装在树莓派上,并在三个Pi集群中工作.然而,在master ...

  10. netty实现消息中心(一)思路整理

    一.需求 需要实现直播间的以下功能: 群发消息(文本.图片.推荐商品) 点对点私发消息(文本.图片.推荐商品) 单个用户禁言 全体用户禁言 撤回消息 聊天记录持久化 二.技术实现 服务端消息中心采用n ...