美文网首页
Java安全相关及其他内容

Java安全相关及其他内容

作者: yohim | 来源:发表于2022-06-21 11:19 被阅读0次

Java安全

Q:如果没有 Cookie 的话 Session 还能用吗?

一般是通过 Cookie 来保存 SessionID ,假如你使用了 Cookie 保存 SessionID 的方案的话, 如果客户端禁用了 Cookie,那么 Session 就无法正常工作。

但是,并不是没有 Cookie 之后就不能用 Session 了,比如你可以将 SessionID 放在请求的 url 里面https://javaguide.cn/?Session_id=xxx 。这种方案的话可行,但是安全性和用户体验感降低。当然,也可以对 SessionID 进行一次加密之后再传入后端。

认证授权 基础

认证 (Authentication) 和授权 (Authorization)的区别

  • Authentication(认证) 是验证您的身份的凭据(例如用户名/用户 ID 和密码),通过这个凭据,系统得以知道你就是你,也就是说系统存在你这个用户。所以,Authentication 被称为身份/用户验证。

  • Authorization(授权) 发生在 Authentication(认证) 之后。授权嘛,光看意思大家应该就明白,它主要掌管我们访问系统的权限。比如有些特定资源只能具有特定权限的人才能访问比如 admin,有些对系统资源操作比如删除、添加、更新只能特定人才具有。

RBAC

系统权限控制最常采用的访问控制模型就是 RBAC 模型

RBAC 即基于角色的权限访问控制(Role-Based Access Control)。这是一种通过角色关联权限,角色同时又关联用户的授权的方式。

简单地说:一个用户可以拥有若干角色,每一个角色又可以被分配若干权限,这样就构造成“用户-角色-权限” 的授权模型。在这种模型中,用户与角色、角色与权限之间构成了多对多的关系

JWT

JWT (JSON Web Token) 是目前最流行的跨域认证解决方案,是一种基于 Token 的认证授权机制。 从 JWT 的全称可以看出,JWT 本身也是 Token,一种规范化之后的 JSON 结构的 Token。

JWT 自身包含了身份验证所需要的所有信息,因此,我们的服务器不需要存储 Session 信息。这显然增加了系统的可用性和伸缩性,大大减轻了服务端的压力。

并且, 使用 JWT 认证可以有效避免 CSRF 攻击,因为 JWT 一般是存在在 localStorage 中,使用 JWT 进行身份验证的过程中是不会涉及到 Cookie 的。

JWT组成部分

JWT 本质上就是一组字串,通过(.)切分成三个为 Base64 编码的部分:

  • Header : 描述 JWT 的元数据,定义了生成签名的算法以及 Token 的类型。

    JSON 形式的Header 通常由两部分组成:

    • typ(Type):令牌类型,也就是 JWT。

    • alg(Algorithm) :签名算法,比如 HS256。

  • Payload : 用来存放实际需要传递的数据

    Payload 也是 JSON 格式数据,其中包含了 Claims(声明,包含 JWT 的相关信息)。

    Claims 分为三种类型:

    • Registered Claims(注册声明) :预定义的一些声明,建议使用,但不是强制性的。

    • Public Claims(公有声明) :JWT 签发方可以自定义的声明,但是为了避免冲突,应该在 IANA JSON Web Token Registryopen in new window 中定义它们。

    • Private Claims(私有声明) :JWT 签发方因为项目需要而自定义的声明,更符合实际项目场景使用。

    下面是一些常见的注册声明:

    • iss(issuer):JWT 签发方。

    • iat(issued at time):JWT 签发时间。

    • sub(subject):JWT 主题。

    • aud(audience):JWT 接收方。

    • exp(expiration time):JWT 的过期时间。

    • nbf(not before time):JWT 生效时间,早于该定义的时间的 JWT 不能被接受处理。

    • jti(JWT ID):JWT 唯一标识。

    Payload 部分默认是不加密的,一定不要将隐私信息存放在 Payload 当中

  • Signature(签名) :服务器通过 Payload、Header 和一个密钥(Secret)使用 Header 里面指定的签名算法(默认是 HMAC SHA256)生成。

    Signature 部分是对前两部分的签名,作用是防止 JWT(主要是 payload) 被篡改。

    这个签名的生成需要用到:

    • Header + Payload。

    • 存放在服务端的密钥(一定不要泄露出去)。

    • 签名算法。

    签名的计算公式如下:

        HMACSHA256(
        base64UrlEncode(header) + "." +
        base64UrlEncode(payload),
        secret)
    

    算出签名以后,把 Header、Payload、Signature 三个部分拼成一个字符串,每个部分之间用"点"(.)分隔,这个字符串就是 JWT

JWT 通常是这样的:xxxxx.yyyyy.zzzzz

如何基于 JWT 进行身份验证?

在基于 JWT 进行身份验证的的应用程序中,服务器通过 Payload、Header 和 Secret(密钥)创建 JWT 并将 JWT 发送给客户端。客户端接收到 JWT 之后,会将其保存在 Cookie 或者 localStorage 里面,以后客户端发出的所有请求都会携带这个令牌。

简化后的步骤如下:

  1. 用户向服务器发送用户名、密码以及验证码用于登陆系统。

  2. 如果用户用户名、密码以及验证码校验正确的话,服务端会返回已经签名的 Token,也就是 JWT。

  3. 用户以后每次向后端发请求都在 Header 中带上这个 JWT 。

  4. 服务端检查 JWT 并从中获取用户相关信息。

两点建议:

  1. 建议将 JWT 存放在 localStorage 中,放在 Cookie 中会有 CSRF 风险。

  2. 请求服务端并携带 JWT 的常见做法是将其放在 HTTP Header 的 Authorization 字段中(Authorization: Bearer Token)。

如何防止 JWT 被篡改?

有了签名之后,即使 JWT 被泄露或者解惑,黑客也没办法同时篡改 Signature 、Header 、Payload。

这是为什么呢?因为服务端拿到 JWT 之后,会解析出其中包含的 Header、Payload 以及 Signature 。服务端会根据 Header、Payload、密钥再次生成一个 Signature。拿新生成的 Signature 和 JWT 中的 Signature 作对比,如果一样就说明 Header 和 Payload 没有被修改。

不过,如果服务端的秘钥也被泄露的话,黑客就可以同时篡改 Signature 、Header 、Payload 了。黑客直接修改了 Header 和 Payload 之后,再重新生成一个 Signature 就可以了。

密钥一定保管好,一定不要泄露出去。JWT 安全的核心在于签名,签名安全的核心在密钥。

如何加强 JWT 的安全性?

  1. 使用安全系数高的加密算法。

  2. 使用成熟的开源库,没必要造轮子。

  3. JWT 存放在 localStorage 中而不是 Cookie 中,避免 CSRF 风险。

  4. 一定不要将隐私信息存放在 Payload 当中。

  5. 密钥一定保管好,一定不要泄露出去。JWT 安全的核心在于签名,签名安全的核心在密钥。

  6. Payload 要加入 exp (JWT 的过期时间),永久有效的 JWT 不合理。并且,JWT 的过期时间不易过长。

JWT 的优势

相比于 Session 认证的方式来说,使用 JWT 进行身份认证主要有下面 4 个优势

  • 无状态

    JWT 自身包含了身份验证所需要的所有信息,因此,我们的服务器不需要存储 Session 信息。这显然增加了系统的可用性和伸缩性,大大减轻了服务端的压力。

    不过,也正是由于 JWT 的无状态,也导致了它最大的缺点:不可控!

  • 有效避免了 CSRF 攻击

    CSRF(Cross Site Request Forgery) 一般被翻译为 跨站请求伪造,属于网络攻击领域范围。相比于 SQL 脚本注入、XSS 等安全攻击方式,CSRF 的知名度并没有它们高。

    CSRF 攻击需要依赖 Cookie ,Session 认证中 Cookie 中的 SessionID 是由浏览器发送到服务端的,只要发出请求,Cookie 就会被携带。借助这个特性,即使黑客无法获取你的 SessionID,只要让你误点攻击链接,就可以达到攻击效果。

    Q:为什么 JWT 不会存在这种问题呢?

    一般情况下我们使用 JWT 的话,在我们登录成功获得 JWT 之后,一般会选择存放在 localStorage 中。前端的每一个请求后续都会附带上这个 JWT,整个过程压根不会涉及到 Cookie。因此,即使你点击了非法链接发送了请求到服务端,这个非法请求也是不会携带 JWT 的,所以这个请求将是非法的。

    总结来说就一句话:使用 JWT 进行身份验证不需要依赖 Cookie ,因此可以避免 CSRF 攻击。

  • 适合移动端应用

    使用 Session 进行身份认证的话,需要保存一份信息在服务器端,而且这种方式会依赖到 Cookie(需要 Cookie 保存 SessionId),所以不适合移动端。

    但是,使用 JWT 进行身份认证就不会存在这种问题,因为只要 JWT 可以被客户端存储就能够使用,而且 JWT 还可以跨语言使用。

  • 单点登录友好

    使用 Session 进行身份认证的话,实现单点登录,需要我们把用户的 Session 信息保存在一台电脑上,并且还会遇到常见的 Cookie 跨域的问题。但是,使用 JWT 进行认证的话, JWT 被保存在客户端,不会存在这些问题。

SSO单点登录

SSO英文全称Single Sign On,单点登录。SSO是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。

单点登录系统的好处:

  1. 用户角度 :用户能够做到一次登录多次使用,无需记录多套用户名和密码,省心。

  2. 系统管理员角度 : 管理员只需维护好一个统一的账号中心就可以了,方便。

  3. 新系统开发角度: 新系统开发时只需直接对接统一的账号中心即可,简化开发流程,省时。

核心应用与依赖

应用/模块/对象 说明
前台站点 需要登录的站点
SSO站点-登录 提供登录的页面
SSO站点-登出 提供注销登录的入口
SSO服务-登录 提供登录服务
SSO服务-登录状态 提供登录状态校验/登录信息查询的服务
SSO服务-登出 提供用户注销登录的服务
数据库 存储用户账户信息
缓存 存储用户的登录信息,通常使用Redis

用户登录状态的存储与校验

用户登录成功之后,生成AuthToken交给客户端保存。如果是浏览器,就保存在Cookie中。如果是手机App就保存在App本地缓存中。用户在浏览需要登录的页面时,客户端将AuthToken提交给SSO服务校验登录状态/获取用户登录信息

对于登录信息的存储,建议采用Redis,使用Redis集群来存储登录信息,既可以保证高可用,又可以线性扩充。同时也可以让SSO服务满足负载均衡/可伸缩的需求。

对象 说明
AuthToken 直接使用UUID/GUID即可,如果有验证AuthToken合法性需求,可以将UserName+时间戳加密生成,服务端解密之后验证合法性
登录信息 通常是将UserId,UserName缓存起来

用户登录/登录校验

用户登录后AuthToken保存在Cookie中。然后通过SSO服务,完成对用户状态的校验/用户登录信息的获取

用户登出

用户登出时要做的事情很简单:

  1. 服务端清除缓存(Redis)中的登录状态

  2. 客户端清除存储的AuthToken

跨域登录、登出

由于客户端是将AuthToken存储在Cookie中的。所以跨域要解决的问题,就是如何解决Cookie的跨域读写问题。

解决跨域的核心思路就是:

  • 登录完成之后通过回调的方式,将AuthToken传递给主域名之外的站点,该站点自行将AuthToken保存在当前域下的Cookie中。

  • 登出完成之后通过回调的方式,调用非主域名站点的登出页面,完成设置Cookie中的AuthToken过期的操作。

  • 跨域登录(主域名已登录)

Java定时任务

单机定时任务技术选型

Timer

java.util.Timer是 JDK 1.3 开始就已经支持的一种定时任务的实现方式。

Timer 内部使用一个叫做 TaskQueue 的类存放定时任务,它是一个基于最小堆实现的优先级队列。TaskQueue 会按照任务距离下一次执行时间的大小将任务排序,保证在堆顶的任务最先执行。这样在需要执行任务时,每次只需要取出堆顶的任务运行即可!

不过其缺陷较多,比如一个 Timer 一个线程,这就导致 Timer 的任务的执行只能串行执行,一个任务执行时间过长的话会影响其他任务(性能非常差),再比如发生异常时任务直接停止(Timer 只捕获了 InterruptedException )。

ScheduledThreadPoolExecutor 支持多线程执行定时任务并且功能更强大,是 Timer 的替代品。

ScheduledExecutorService

ScheduledExecutorService 是一个接口,有多个实现类,比较常用的是 ScheduledThreadPoolExecutor

ScheduledThreadPoolExecutor 本身就是一个线程池,支持任务并发执行。并且,其内部使用 DelayQueue 作为任务队列。

不论是使用 Timer 还是 ScheduledExecutorService 都无法使用 Cron 表达式指定任务执行的具体时间。

Spring Task

直接通过 Spring 提供的 @Scheduled 注解即可定义定时任务,非常方便!

Spring Task 支持 Cron 表达式 的。Cron 表达式主要用于定时作业(定时任务)系统定义执行时间或执行频率的表达式,

但是,Spring 自带的定时调度只支持单机,并且提供的功能比较单一。

Spring Task 底层是基于 JDK 的 ScheduledThreadPoolExecutor 线程池来实现的。

优缺点总结:

  • 优点: 简单,轻量,支持 Cron 表达式

  • 缺点 :功能单一

时间轮

Kafka、Dubbo、ZooKeeper、Netty 、Caffeine 、Akka 中都有对时间轮的实现。

时间轮简单来说就是一个环形的队列(底层一般基于数组实现),队列中的每一个元素(时间格)都可以存放一个定时任务列表。

时间轮中的每个时间格代表了时间轮的基本时间跨度或者说时间精度,加入时间一秒走一个时间格的话,那么这个时间轮的最高精度就是 1 秒(也就是说 3 s 和 3.9s 会在同一个时间格中)。

分布式定时任务技术选型

如果我们需要一些高级特性比如支持任务在分布式场景下的分片和高可用的话,我们就需要用到分布式任务调度框架了。

通常情况下,一个定时任务的执行往往涉及到下面这些角色:

  • 任务 : 首先肯定是要执行的任务,这个任务就是具体的业务逻辑比如定时发送文章。

  • 调度器 :其次是调度中心,调度中心主要负责任务管理,会分配任务给执行器。

  • 执行器 : 最后就是执行器,执行器接收调度器分派的任务并执行。

Quartz

一个很火的开源任务调度框架,完全由Java写成。Quartz 可以说是 Java 定时任务领域的老大哥或者说参考标准,其他的任务调度框架基本都是基于 Quartz 开发的

使用 Quartz 可以很方便地与 Spring 集成,并且支持动态添加任务和集群。但是,Quartz 使用起来也比较麻烦,API 繁琐。

并且,Quzrtz 并没有内置 UI 管理控制台,不过可以使用 quartzuiopen in new window 这个开源项目来解决这个问题。

另外,Quartz 虽然也支持分布式任务。但是,它是在数据库层面,通过数据库的锁机制做的,有非常多的弊端比如系统侵入性严重、节点负载不均衡。有点伪分布式的味道。

优缺点总结:

  • 优点: 可以与 Spring 集成,并且支持动态添加任务和集群。

  • 缺点 :分布式支持不友好,没有内置 UI 管理控制台、使用麻烦(相比于其他同类型框架来说)

Elastic-Job

ElasticJob 支持任务在分布式场景下的分片和高可用、任务可视化管理等功能。

Elastic-Job 没有调度中心这一概念,而是使用 ZooKeeper 作为注册中心,注册中心负责协调分配任务到不同的节点上。

Elastic-Job 中的定时调度都是由执行器自行触发,这种设计也被称为去中心化设计(调度和处理都是执行器单独完成)。

优缺点总结:

  • 优点 :可以与 Spring 集成、支持分布式、支持集群、性能不错

  • 缺点 :依赖了额外的中间件比如 Zookeeper(复杂度增加,可靠性降低、维护成本变高)

XXL-JOB

XXL-JOB 于 2015 年开源,是一款优秀的轻量级分布式任务调度框架,支持任务可视化管理、弹性扩容缩容、任务失败重试和告警、任务分片等功能,

XXL-JOB调度中心执行器 两大部分组成。调度中心主要负责任务管理、执行器管理以及日志管理。执行器主要是接收调度信号并处理。另外,调度中心进行任务调度时,是通过自研 RPC 来实现的。

不同于 Elastic-Job 的去中心化设计, XXL-JOB 的这种设计也被称为中心化设计(调度中心调度多个执行器执行任务)。

Quzrtz 类似 XXL-JOB 也是基于数据库锁调度任务,存在性能瓶颈。不过,一般在任务量不是特别大的情况下,没有什么影响的。

实际上,我们要用 XXL-JOB 的话,只需要重写 IJobHandler 自定义任务执行逻辑就可以了,非常易用!还可以直接基于注解定义任务。

优缺点总结:

  • 优点:开箱即用(学习成本比较低)、与 Spring 集成、支持分布式、支持集群、内置了 UI 管理控制台。

  • 缺点:不支持动态添加任务

PowerJob

非常值得关注的一个分布式任务调度框架,分布式任务调度领域的新星

相关文章

  • Java安全相关及其他内容

    Java安全 Q:如果没有 Cookie 的话 Session 还能用吗? 一般是通过 Cookie 来保存 Se...

  • 任务清单丨小程序java云服务器配置123

    以阿里云服务器为案例,后台使用java 及java web server 相关技术,其他方案基本类同 root账户...

  • App设计细节

    界面安全 所有输入密码及安全相关的内容都要设置secureTextEntry属性为YES. 当用户显示如信用卡号或...

  • Java实战知识点总结【真材实料】

    点关注,不迷路;持续更新Java架构相关技术及资讯热文!!! 阶段一:实训内容(J2SE) Java语法; 变量,...

  • Android 高级面试-3:语言相关

    主要内容:Kotlin, Java, RxJava, 多线程/并发, 集合 1、Java 相关 1.1 缓存相关 ...

  • 深入学习java系列之集合框架

    集合概述 java 集合部分主要有 java 集合框架相关的内容和 java 泛型相关的内容。 集合是用来做什么的...

  • ITIL及安全相关

    https://www.jianshu.com/p/00dbc482d5d0 http://www.pangxie...

  • 线程安全及相关

    什么是线程安全堆内存中的数据由于可以被任何线程所访问,在没有限制的情况下存在被意外修改的风险。即堆内存空间在没有保...

  • java 反射初步理解

    前言   之前整理了java同步的相关内容,现在开始整理java反射,都属于java相关内容。在查找资料的过程中,...

  • 内容安全及防护

    课程概览 课时列表 评价 笔记 课时1:移动APP风险现状25:08 课时2:常见APP风险及检测21:59 课时...

网友评论

      本文标题:Java安全相关及其他内容

      本文链接:https://www.haomeiwen.com/subject/ylzyvrtx.html