上一篇已经讲了微服务组件中的 路由网关(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); } }
|
- 服务认证暴力破解工具Crowbar
服务认证暴力破解工具Crowbar Crowbar是Kali Linux新增的一款服务认证暴力破解工具.该工具支持OpenVPN.RDP.SSH和VNC服务.该工具具备常见的暴力破解功能,如主机字 ...
- SpringCloud学习笔记(5)----Spring Cloud Netflix之Eureka的服务认证和集群
1. Eureka服务认证 1. 引入依赖 <dependency> <groupId>org.springframework.boot</groupId> < ...
- asp.net core系列 60 Ocelot 构建服务认证示例
一.概述 在Ocelot中,为了保护下游api资源,用户访问时需要进行认证鉴权,这需要在Ocelot 网关中添加认证服务.添加认证后,ReRoutes路由会进行身份验证,并使用Ocelot的基于声明的 ...
- 二.3.token认证,jwt认证,前端框架
一.token: 铺垫: 之前用的是通过最基本的用户名密码登录我的运维平台http://127.0.0.1:8000/---这种用的是form表单,但是这种对于前后端分离的不适合.前后端分离,应该通过 ...
- H3C交换机telnet服务认证模式配置
以H3C交换机为例,介绍telnet服务的三种认证方式配置(none无需认证,password密码认证,scheme账户+密码认证) None认证模式配置步骤:[H3C]telnet server e ...
- 服务安全-JWT(JSON Web Tokens):百科
ylbtech-服务安全-JWT(JSON Web Tokens):百科 JSON Web Tokens是一种开放的行业标准 RFC 7519方法,用于在双方之间安全地表示索赔. JWT.IO允许您解 ...
- (9)学习笔记 ) ASP.NET CORE微服务 Micro-Service ---- JWT算法
一. JWT 简介 内部 Restful 接口可以“我家大门常打开”,但是如果要给 app 等使用的接口,则需要做权限校验,不能谁都随便调用. Restful 接口不是 web 网站,App 中很难直 ...
- 苹果登录服务端JWT算法验证-PHP
验证参数 可用的验证参数有 userID.authorizationCode.identityToken,需要iOS客户端传过来 验证方式 苹果登录验证可以选择两种验证方式 具体可参考这篇文章 htt ...
- 微服务统一登陆认证怎么做?JWT ?
无状态登录原理 1.1.什么是有状态? 有状态服务,即服务端需要记录每次会话的客户端信息,从而识别客户端身份,根据用户身份进行请求的处理,典型的设计如tomcat中的session. 例如登录:用户登 ...
随机推荐
- Java实现 LeetCode 388 文件的最长绝对路径
388. 文件的最长绝对路径 假设我们以下述方式将我们的文件系统抽象成一个字符串: 字符串 "dir\n\tsubdir1\n\tsubdir2\n\t\tfile.ext" 表示 ...
- CSDN如何获得2020技术圈认证(新徽章哦)
打开CSDN APP 然后登陆上就可以了 把这些看完了就可以了
- 第八届蓝桥杯JavaC组省赛真题
解题代码部分来自网友,如果有不对的地方,欢迎各位大佬评论 题目1.外星日历 题目描述 某星系深处发现了文明遗迹. 他们的计数也是用十进制. 他们的文明也有日历.日历只有天数,没有年.月的概念. 有趣的 ...
- IDEA2020年激活码
若出现无法使用,激活失败的情况,可以关注公众号:凌晨四点的程序员,回复消息"IDEA"持续更新最新激活码 2020年05月26更新(2020年6月份) OI7FTW2137-eyJ ...
- JAVA第三次blog总结
JAVA第三次blog总结 0.前言 这是我们在博客园上第三次写博客,也是本学期最后一次的JAVA学习大总结.现在我们的JAVA已经接近尾声了,对于编程思想和方法的改变依旧是难点,但是经过这一段时间的 ...
- FTP配置多用户多目录多权限
环境介绍 根据开发的需求 要求创建FTP服务器,把前端和后端分开用不同的FTP账号 系统环境 centos 7.4 selinux 关闭 防火墙关闭 安装FTP 很简单就一条命令 yum instal ...
- zabbix 磁盘分区监控
系统环境 Zabbix 版本:3.4 操作系统版本:centos7.4 监控分区 / ./boot ./home 先创建监控项的模板 点击创建监控项 名称随意填写,键值的话因为我们监控车的是磁盘剩 ...
- Android开发之StrictMode
StrictMode简介 Android开发者官网对StrictMode的介绍 StrictMode类是Android 2.3(API 9)引入的一个工具类 用来帮助开发者发现代码中一些不规范的问题, ...
- SignalR控制台自托管服务端向web客户端指定用户推送数据,客户端断线重连
一.前言 SignalR是微软推出的开源实时通信框架.其内部使用Web Socket, Server Sent Events 和 Long Polling作为底层传输方式,SignalR会根据客户端和 ...
- Hexo博客框架攻略
前言 前天无意在b站看到up主CodeSheep上传的博客搭建教程,引起了我这个有需求但苦于没学过什么博客框架的小白的兴趣.于是花了两天时间终于终于把自己的博客搭建好了,踩了无数的坑,走偏了无数的路, ...