关于认证授权:Spring认证中国教育管理中心Spring-Data-Neo4j教程一

原题目:Spring认证中国教育管理中心-Spring Data Neo4j教程一(Spring中国教育管理中心) Spring认证-Spring Data Neo4j教程一 开始咱们为 SDN 提供了 Spring Boot 启动器。请通过您的依赖治理蕴含启动模块并配置要应用的螺栓 URL,例如org.neo4j.driver.uri=bolt://localhost:7687. 启动器假设服务器已禁用身份验证。因为 SDN 启动器依赖于 Java 驱动程序的启动器,因而无关配置的所有内容在此处也实用。无关可用属性的参考,请应用org.neo4j.driver命名空间中的 IDE 主动实现性能或查看专用手册。SDN反对 家喻户晓和了解的命令式编程模型(很像 Spring Data JDBC 或 JPA)基于Reactive Streams的反应式编程,包含对反应式事务的齐全反对。这些都蕴含在同一个二进制文件中。反应式编程模型在数据库端须要 4.0 Neo4j 服务器,另一方面须要反应式 Spring。 5.1筹备数据库对于这个例子,咱们停留在movie graph 中,因为它随每个 Neo4j 实例收费提供。 如果您没有正在运行的数据库但装置了 Docker,请运行: 清单 1. 在 Docker 中启动一个本地 Neo4j 实例。 docker run --publish=7474:7474 --publish=7687:7687 -e 'NEO4J_AUTH=neo4j/secret' neo4j:4.3.6您当初能够拜访http://localhost:7474。下面的命令将服务器的明码设置为secret. :play movies请留神在提示符 ( )中筹备好运行的命令。执行它以用一些测试数据填充您的数据库。 5.2.创立一个新的 Spring Boot 我的项目设置 Spring Boot 我的项目的最简略办法是start.spring.io (它也集成在次要 IDE 中,以防您不想应用该网站)。 抉择“Spring Web Starter”以获取创立基于 Spring 的 Web 应用程序所需的所有依赖项。Spring Initializr 将负责为您创立一个无效的我的项目构造,其中蕴含所选构建工具的所有文件和设置。 ...

January 17, 2022 · 3 min · jiezi

关于认证授权:Spring认证中国教育管理中心Apache-Geode-的-Spring-数据教程三

原题目:Spring认证中国教育管理中心-Apache Geode 的 Spring 数据教程三(Spring中国教育管理中心) 5.4.2.配置 Apache Geode CacheServerSpring Data for Apache Geode 包含对配置CacheServer 的专用反对 ,容许通过 Spring 容器进行残缺配置,如以下示例所示: <?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:gfe="https://www.springframework.org/schema/geode" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsdhttps://www.springframework.org/schema/geode https://www.springframework.org/schema/geode/spring-geode.xsd"> <gfe:cache/> <!-- Example depicting serveral Apache Geode CacheServer configuration options --> <gfe:cache-server id="advanced-config" auto-startup="true" bind-address="localhost" host-name-for-clients="localhost" port="${gemfire.cache.server.port}" load-poll-interval="2000" max-connections="22" max-message-count="1000" max-threads="16" max-time-between-pings="30000" groups="test-server"><gfe:subscription-config eviction-type="ENTRY" capacity="1000" disk-store="file://${java.io.tmpdir}"/></gfe:cache-server> <context:property-placeholder location="classpath:cache-server.properties"/> </beans>Spring认证中国教育管理中心-Apache Geode 的 Spring 数据教程三后面的配置显示了cache-server元素和许多可用选项。 这个配置不是对端口进行硬编码,而是应用 Spring 的 上下文 命名空间来申明一个property-placeholder. 一个 属性占位符 读取一个或多个属性文件,而后在运行时值替换属性的占位符。这样做能够让管理员更改值而无需接触主应用程序配置。Spring 还提供 SpEL 和环境形象, 以反对从主代码库中将特定于环境的属性内部化,从而简化跨多台机器的部署。 为防止初始化问题,CacheServerSpring Data for Apache Geode 的启动会在Spring 容器齐全初始化后启动。这样做能够让以申明形式定义的潜在区域、侦听器、编写器或实例化器在服务器开始承受连贯之前齐全初始化和注册。在以编程形式配置这些元素时请记住这一点,因为服务器可能在您的组件之前启动,因而不会被立刻连贯的客户端看到。 ...

December 20, 2021 · 3 min · jiezi

关于认证授权:以实例说明-OAuth2

1 概述OAuth2 是互联网中宽泛应用的受权规范,罕用于实现单点登录、第三方受权。尽管以后有 更欠缺的流程,但国内次要还是应用OAuth规范。国内一些服务商的OAuth是本人批改过的, 没有按照规范文档实现,常常产生规范库没方法实现受权的状况,不知意欲何为,应用时还 是须要按照服务商的开发文档。 OAuth2 解决的问题是第三方利用受权的问题,也能够用于一个宏大公司内多个零碎应用统 一账号的状况。它假如零碎中受权服务器是独立的,对所有服务的拜访必须首先通过受权服 务器的受权。本文总是先提出一个系统结构,再思考应用OAuth2规范解决其中受权的问题。 会啰嗦一些,网上有许多简短精炼的介绍,或者间接看rfc6749,都能够满足想要迅速理解 OAuth2规范的人。 Google 有个 OAuth 2.0 Playgroud 能够模仿他家各服务的 oauth2 受权过程。我也做了个 相似的,尝试应用别家 OAuth2 API 时能够应用。 2 Authorization Code Grant2.1 场景阐明:第三方受权这个场景是 OAuth2 最常呈现的场景,以 Github 为例。假如当初有一个第三方服务,他的 性能是帮用户定期检查 Github 账户上的 Repo 是否上传了敏感信息,如密钥,数据库口令 等。 因为是第三方服务,且服务须要拜访用户的 Repo 文件,属于个人信息,Github 和第三方 服务都有任务征求用户批准。所以第三方服务须要取得用户受权,能力拜访用户的 Repo, 而 Github 须要取得用户受权,能力让第三方服务解决用户的资源。 程序构造如下图。三方服务向 Github 的受权服务器申请受权,受权服务器征求用户批准后, 容许三方利用拜访资源服务器中用户的 Repo。 2.2 第三方受权解决方案在解释如何应用 OAuth2 实现这个流程之前,先理解一些 OAuth2 中常见的几个申请/返回参数。 access_token: OAuth2 流程最初产生的受权码,领有受权码即可拜访资源,通常是 JWT 格局。refresh_token: access_token 是有时效的,能够应用 refresh_token 申请新的 access_token 。 refresh_token 的生成和验证都在受权服务器上,所以个别不应用 JWT 格局,而是一个在受权服务器中能够查问到具体受权信息和状态的随机字符串。code: OAuth2 的两头过程,示意用户不久前确认了这个受权,能够通过 code 获取 access_token 。Github 的 OAuth2 治理三方登陆计划如下图。 ...

September 6, 2021 · 2 min · jiezi

关于认证授权:新版-ISO-22301-全解读

ISO 22301业务连续性管理体系,自其公布以来该体系不断完善以领导企业更好地落地施行,保障业务继续不间断运行。目前共有两个版本,2019版与2012版。 2019 新版针对 2012 旧版而言到底有什么变动?对于业务连续性及行业到底又有什么影响? 解读之前,先回顾一下,什么是 ISO 22301 ? ISO 22301 是寰球首个基于组织业务连续性治理(Business Continuity Management,简称BCM)的国际标准,旨在帮忙组织建设预案和确保其业务在面对内部威逼时可能继续运行,例如自然灾害或者信息安全破绽。 ISO 22301业务连续性管理体系,可能帮忙企业制订一套一体化的治理流程打算,使企业对潜在的劫难加以分别剖析,帮忙其确定可能产生的冲击对企业运作造成的威逼,并提供一个无效的管理机制来阻止或对消这些威逼,缩小劫难事件给企业带来损失。 以下内容为同创征询专家樊宇整顿编写: ISO 22301-2019 新版规范的变动最新版本IS0 22301:2019的要害改良更偏向于构造和术语,以增强对内容的更好了解,并使其与其它 ISO 管理体系规范保持一致。 自2012年首次公布以来,ISO 22301规范已成为业务连续性管理体系的国际标准。依据 ISO 考察,已有超过4000个组织持有了 ISO 22301证书。该规范的受欢迎水平曾经在多个行业中疾速流传,如银行、保险、证券、化工厂、IT服务提供商以及汽车零件制造商等。 思考到这种流行性,联合其历年来的理论应用成果。ISO 22301新版本于2019年11月正式公布。 变动是无限的如果您曾经通过 ISO 22301:2012认证,那么过渡到ISO 22301:2019根本没有任何问题。ISO 22301:2019版与2012版比照表明,ISO 22301:2019规范并没有重大的构造更改。 在过来的几年中,对 ISO 管理体系规范进行订正时,有挑战性的次要起因之一为其高级构造,它是所有 ISO 管理体系规范的对立构造和外围文本。然而,2012年版的ISO 22301:2012曾经具备高级构造,这是最早采纳这种新构造的 ISO 规范之一。 因而,工作组不用重写整个规范,而能够专一于措辞和清晰度。缩小了许多多余的局部,定义变得更加统一,用词变得更加合乎逻辑。 回到BCM的实质很多要求曾经被还原到 BCM 的实质上。 第4.1节就是一个很好的例子:ISO 22301:2012年版规定了组织须要做些什么(和记录了什么)能力了解组织及其背景,而新版 ISO 22301:2019仅阐明了 “确定内部和外部问题”的须要,而没有具体阐明这必须要怎么做。ISO 22301:2019没有说须要思考哪些方面,也没有包含记录该过程的要求。对于沟通的第7.4节产生了相似的事件,这阐明新版本IS0 22301:2019的要求明显降低。 另一个已削减的要求是最高管理者的参加(5.2)。ISO 22301:2012旧版本甚至要求高层管理人员“积极参与演练和测试“,但 ISO 22301:2019新版本的办法更为实用,并着重于放弃 BCMS 的有效性。 ISO 22301:2019 其余变动除了大量的轻微调整,对申请认证企业的影响很小或没有影响的同时,还有一些值得一提的变动: 第6.3条是很少的新要求之一,它要求组织"以打算的形式”对 BCMS 进行更改。只管从技术上讲此要求是新的,但该条款的内容对于任何人都不应该感到诧异,也很容易以及应该做到。 ...

April 2, 2021 · 1 min · jiezi

深度理解token

1、token是什么?token是一种用户标识,表示用户身份,类似于我们的身份证件。Token 是在服务端产生的。如果前端使用用户名/密码向服务端请求认证,服务端认证成功,那么在服务端会返回 Token 给前端。前端可以在每次请求的时候带上 Token 证明自己的合法地位。如果这个 Token 在服务端持久化(比如存入数据库),那它就是一个永久的身份令牌。 2、为什么要使用token?先来了解一下token认证与session认证的区别:a) Session认证Session认证的方案为:第一次用户认证请求通过时,服务器端存储用户的登录信息,然后在响应时传递给浏览器,浏览器保存为cookie,下次请求时把cookie发送给服务器,服务器根据cookie信息来识别是哪个用户。Session认证适用于单个服务器的场合,如果用户增多,需要部署多个服务器时,Session认证就会暴露问题。因为每个用户在认证后,服务器都会记录Session,一般保存在内存中,随着认证用户增多,服务器开销也会增大;而且认证用户的后续请求都需要到这台保存了自己Session的服务器上验证Cookie。在多集群分布式的场合,这就造成了性能瓶颈,对负载均衡、应用的扩展都会造成影响。(不能多服务共享)进程外Session通过将Session保存在数据库或硬盘中可以解决服务器内存开销的问题,但仍然不便于扩展。 b) Token认证Token认证也是无状态的,不需要在服务端保存认证用户的信息,它的认证流程为:用户使用用户名密码请求服务器;服务器验证用户,如果验证通过就发给用户一个token;客户端保存token,并在之后的每次请求都附上token;服务端验证token、返回数据。Token认证机制不需要保存认证用户的信息、也不需要考录用户在那台服务器登录,可以很好地适应应用的扩展(可以多服务共享)。token的体积很小,请求服务端时,可以被附在URL、Header或是Post参数中。而且token中包含了用户相关的必要、非私密的相信,免去了服务端再次查询数据库的开销另外,token认证能解决以下问题: Token 完全由应用管理,所以它可以避开同源策略Token 可以避免 CSRF 攻击Token 可以是无状态的,可以在多个服务间共享现在详细说明以下roken为什么能够避免CSRF攻击呢?CSRF攻击又是什么意思? 2.1、CSRF攻击CSRF(Cross-site request forgery)也被称为 one-click attack或者 session riding,中文全称是叫跨站请求伪造。一般来说,攻击者通过伪造用户的浏览器的请求,向访问一个用户自己曾经认证访问过的网站发送出去,使目标网站接收并误以为是用户的真实操作而去执行命令。常用于盗取账号、转账、发送虚假消息等。攻击者利用网站对请求的验证漏洞而实现这样的攻击行为,网站能够确认请求来源于用户的浏览器,却不能验证请求是否源于用户的真实意愿下的操作行为。 2.2、token如何避免CSRF攻击CSRF 攻击之所以能够成功,是因为黑客可以完全伪造用户的请求,该请求中所有的用户验证信息都是存在于 cookie 中,因此黑客可以在不知道这些验证信息的情况下直接利用用户自己的 cookie 来通过安全验证。要抵御 CSRF,关键在于在请求中放入黑客所不能伪造的信息,并且该信息不存在于 cookie 之中。可以在 HTTP 请求中以参数的形式加入一个随机产生的 token,并在服务器端建立一个拦截器来验证这个 token,如果请求中没有 token 或者 token 内容不正确,则认为可能是 CSRF 攻击而拒绝该请求。 3、使用JWT生成和验证token以下代码仅仅用于示意,并不是完整版。 3.1、采用HS256算法生成和验证后台生成token: const jwt = require('jsonwebtoken')const fs = require('fs')// Token 数据const payload = { name: 'wanghao', admin: true}/** * HS256 * 用 HS256 算法生成与验证 JWT */// 密钥const secret = 'ILOVENINGHAO'// 签发 Tokenconst token = jwt.sign(payload, secret, { expiresIn: '1day' })// 输出签发的 Tokenconsole.log('HS256 算法:', token)拦截器验证: ...

August 21, 2019 · 2 min · jiezi

时隔五年Scrapyd-终于原生支持-basic-auth

Issue in 2014scrapy/scrapyd/issues/43 Pull request in 2019scrapy/scrapyd/pull/326 试用安装:pip install -U git+https://github.com/my8100/scrapyd.git@add_basic_auth更新配置文件 scrapyd.conf,其余配置项详见官方文档[scrapyd]username = yourusernamepassword = yourpassword启动:scrapydIn [1]: import requestsIn [2]: requests.get('http://127.0.0.1:6800/').status_codeOut[2]: 401In [3]: requests.get('http://127.0.0.1:6800/', auth=('admin', 'admin')).status_codeOut[3]: 401In [4]: requests.get('http://127.0.0.1:6800/', auth=('yourusername', 'yourpassword')).status_codeOut[4]: 200由于 Scrapyd 的 GitHub 最新提交已经重构了 Jobs 页面,如果正在使用 ScrapydWeb 管理 Scrapyd,则需同步更新 ScrapydWeb:pip install -U git+https://github.com/my8100/scrapydweb.gitGitHubmy8100/scrapyd

May 9, 2019 · 1 min · jiezi

mongodb安装与应用

一、安装数据库如果必要可先更新yum包管理,下面以CentOS系统为例:$ yum -y update1、安装Mongodb查看当前系统版本$ cat /etc/redhat-release打开 https://repo.mongodb.org/yum/… ,选择适合自己系统的mongo版本,然后编辑Mongodb安装源,下面以3.6为例:$ sudo vi /etc/yum.repos.d/mongodb-org-3.6.repo编辑内容(根据自己的mongo版本替换下面的3.6字样):[mongodb-org-3.6]name=MongoDB Repositorybaseurl=https://repo.mongodb.org/yum/redhat/$releasever/mongodb-org/3.6/x86_64/gpgcheck=1enabled=1gpgkey=https://www.mongodb.org/static/pgp/server-3.6.asc安装:$ yum install mongodb-org2、修改Mongo配置mongo配置文件路径:/etc/mongod.conf,但如果用mongod命令直接运行(非service运行),此时若不用-f指定配置文件,mongo会使用内在配置,dbpath也默认存放在/data/db下$ vi /etc/mongod.conf#————————————————————————# 可修改数据库存放位置storage: dbPath: /var/lib/mongodb# 若要支持远程连接,需将默认的127.0.0.1改为:net: bindIp: 0.0.0.0# 若需数据库权限认证,开启下面配置:security: authorization: enabled若远程连接工具Robo 3T还是不能连接,需在防火墙增加端口:$ sudo /sbin/iptables -I INPUT -p tcp –dport 27017 -j ACCEPT二、运行数据库1、启动$ service mongod start # 自动使用配置/etc/mongod.conf# 或手动指定配置文件启动:$ sudo mongod -f /etc/mongod.conf –fork # –fork后台运行# 若以权限认证运行$ sudo mongod -f /etc/mongod.conf –auth –fork若启动出现timeout错误时,可查看service里的pid与config里的pid是否相同,不一致则修改:$ sudo vi /usr/lib/systemd/system/mongod.service # 修改pid$ systemctl daemon-reload # 重新加载service服务$ service mongod start2、重启$ service mongod restart3、关闭$ service mongod stop或手动关闭$ sudo mongod -f /etc/mongod.conf –shutdown4、随系统启动默认安装后即随机启动,无须设置$ chkconfig mongod on三、操作数据库1、数据管理1.) 数据库操作# 进入mongo$ mongo# 显示当前数据库,默认定位test数据库> db;# 显示所有数据库> show dbs;# 切换/创建数据库> use xxx;# 从指定主机克隆数据库> db.cloneDatabase(‘主机’);# 从指定主机上复制A库数据到B库> db.copyDatabase(‘A库’, ‘B库’, ‘主机’); # 删除数据库> db.dropDatabase();# 修复数据库> db.repairDatabase();# 查看当前db的链接机器地址> db.getMongo(); # 退出mongo> exit;2.) 集合(表)/记录操作# 显示所有集合> db.getCollectionNames();# 创建集合> db.createCollection(‘users’, {size: 1024, max: 1000}); # 最大1M,1000条# 显示第一条记录> db.users.findOne();# 显示集合下所有记录> db.users.find();# 新增记录> db.users.save({name: ‘wang’, age: 8});# 根据条件查询> db.users.find({age: 8}); # 修改记录(后面两个参数:第一个表示找不到则新建一条,第二个表示更新多条)db.users.update({name: ‘wang’}, {$set: {sex: ‘male’}}, false, true)# 删除记录db.users.remove({age: 8}); 2、用户管理1.) 用户类型 常用的用户类型有:read/readWrite:读写指定数据库 userAdminAnyDatabase:所有数据库的用户管理权限,有分配角色和用户的权限,但没有读写的权限,在admin库授权 readWriteAnyDatabase:所有数据库的读写权限,在admin库授权 root:超级账号,超级权限,在admin库授权2.) 常用用户指令# 显示所有用户> show users;# 新建用户> db.createUser({user:‘用户名’,pwd:‘密码’,roles:[{role:‘角色’,db:‘库’}]});# 若当前数据库与目标库相同,可简写:> db.createUser({user:‘用户名’,pwd:‘密码’,roles:[‘角色’]});# 追加用户权限> db.grantRolesToUser(‘用户名’,[{role:‘角色’,db:‘库’}]);# 修改用户权限> db.updateUser(‘用户名’,{roles:[‘角色1’,‘角色2’]});# 更新密码> db.changeUserPassword(‘用户名’,‘密码’);# 或> db.updateUser(‘用户名’,{pwd:‘密码’});# 删除用户> db.dropUser(‘用户名’);3.) 管理用户创建root、AnyDatabase角色用户、cluster集群等用户只能在admin库下创建> use admin;# 超级管理员> db.createUser({user:‘root’,pwd:‘密码’,roles:[‘root’]});# user管理员账号> db.createUser({user:‘用户名’,pwd:‘密码’,roles:[‘userAdminAnyDatabase’]});# 任意库读写账号> db.createUser({user:‘用户名’,pwd:‘密码’,roles:[‘readWriteAnyDatabase’]};# 普通读写用户(可以在admin库下创建,但认证时也需在admin库)> db.createUser({user:‘用户名’,pwd:‘密码’,roles:[{role:‘readWrite’,db:‘业务库’}]});4.) 普通用户创建mongoDB的权限是跟随库的,用户在哪个库下创建的,则需在哪个库进行auth认证,如果认证库和读写目标库一致,则连接时authSource参数可省略(见第四节:连接数据库)> use xxx;> db.createUser({user:‘用户名’,pwd:‘密码’,roles:[‘readWrite’]);如果提示SCRAM-SHA-256 requires undigested passwords错误,需要加mechanisms,如下:> db.createUser({user:‘用户名’,pwd:‘密码’,roles:[‘readWrite’],mechanisms: [‘SCRAM-SHA-1’]});四、连接数据库1、shell方式连接:1.)普通登录如果mongoDB未开启认证模式,所有用户的权限与root一样,任意操作。$ mongo# 自定义host和端口$ mongo –host 主机 –port 端口2.)认证登录mongoDB的权限是跟随库的,普通用户在哪个库新建授权,就需要在哪个库进行auth验证$ mongo –host 主机 –port 端口 -u ‘用户名’ -p ‘密码’ –authenticationDatabase ‘身份认证所用库’root和AnyDatabase角色的权限只能在admin数据库里认证,如:$ mongo -u root -p 123456 –authenticationDatabase admin也可以进入数据库后再授权:$ mongo> use 认证数据库;> db.auth(‘用户名’, ‘密码’);2、URI方式连接:1.)未认证数据库mongo://主机:端口/数据库名2.)认证数据库a.)普通数据用户若当前用户认证的数据库与要操作的数据库相同,连接方式如下:mongo://用户名:密码@主机:端口/数据库名若不同,则需添加authSource,例如:在admin库里认证了,角色db却是xxxmongo://用户名:密码@主机:端口/xxx?authSource=adminb.)root和所有数据库用户若连接用户是root、*AnyDatabase角色、cluster集群的认证库都是adminmongo://用户名:密码@主机:端口/数据库名?authSource=adminc.)认证机制mongoDB支持多种认证机制, 常用的有’MONGODB-CR’ 和 ‘SCRAM-SHA-1’ 两种,官方推荐 ‘SCRAM-SHA-1’,此时不需要带在URI里,但如果是’MONGODB-CR’类型需加添加在URI上。mongo://用户名:密码@主机:端口/数据库?authMechanism=认证机制&authSource=身份认证所用库 ...

April 19, 2019 · 2 min · jiezi

基于oAuth2的OIDC原理 学习笔记

OAuth2概念OAuth 2.0 规范定义了一个授权(delegation)协议,OAuth2.0 不是认证协议。OAuth解决的大部分问题在于Client和被访问的资源之间的连接上,在用户不存在的情况下,使用这种委托访问。流程上图ABCDE这5个步骤,既是完整的获取访问令牌的一个过程,其中:Client 为第三方应用程序,resource owner 为资源所有者user-agent 为类似浏览器的代理authorization server 授权服务器resource server 资源服务器流程如下:A) Client使用浏览器(User-Agent)访问Authorization server。也就是用浏览器访问一个URL,这个URL是Authorization server提供的,访问的Client需要提供(客户端标识client_id,授权范围scope,本地状态state和redirect_uri)这些参数。B) Authorization server验证Client在(A)中传递的参数信息,如果无误则提供一个页面供Resource owner登陆,登陆成功后选择Client可以访问Resource server的哪些资源以及读写权限。C) 在(B)无误后返回一个授权码(Authorization Code)给Client。D) Client拿着(C)中获得的授权码(Authorization Code)和(客户端标识、redirect_uri等信息)作为参数,请求Authorization server提供的获取access token。E) Authorization server返回access token和可选的refresh token 以及令牌有效时间等信息给Client。F) client拿到access token后就可以去resources server访问resource owner的资源OIDC定义了一种基于OAuth2的用户身份认证OIDC的核心在于在OAuth2的授权流程中,一并提供用户的身份认证信息(ID Token)给到第三方客户端,ID Token使用JWT格式来包装,得益于JWT(JSON Web Token)的自包含性,紧凑性以及防篡改机制,使得ID Token可以安全的传递给第三方客户端程序并且容易被验证。此外还提供了UserInfo的接口,用于获取用户的更完整的信息流程RP发送一个认证请求给OP;OP对EU进行身份认证,然后提供授权;OP把ID Token和Access Token(需要的话)返回给RP;RP使用Access Token发送一个请求UserInfo EndPoint;UserInfo EndPoint返回EU的Claims。术语:EU:End User:一个人类用户。RP:Relying Party ,用来代指OAuth2中的受信任的客户端,身份认证和授权信息的消费方;OP:OpenID Provider,有能力提供EU认证的服务(比如OAuth2中的授权服务),用来为RP提供EU的身份认证信息;ID Token:JWT格式的数据,包含EU身份认证的信息。UserInfo Endpoint:用户信息接口(受OAuth2保护),当RP使用Access Token访问时,返回授权用户的信息,此接口必须使用HTTPSAuthN & AuthZ: AuthN代表authentication AuthZ 代表authrization

March 17, 2019 · 1 min · jiezi

注解认证

问题描述权限认证权限认证一直是比较复杂的问题,如果是实验这种要求不严格的产品,直接逃避掉权限认证。软件设计与编程实践的实验,后台直接用Spring Data REST,好使是好使,但是不能在实际项目中运用,直接把api自动生成了,谁调用都行。在商业项目中,没有权限是不行的。注解关于权限,一直没有找到很好的解决方案。直到网上送检项目,因功能简单,且用户角色单一,潘老师提出了利用注解实现权限认证的方案。两个注解,AdminOnly标注只能给管理员用的方法,Anonymous标注对外的无需认证的接口,其他的未标注的是给普通用户使用的。示例代码示例代码地址:todo开发环境:Java 1.8 + Spring Boot 2.1.2.RELEASE实现拦截器根据三类方法,对用户权限进行拦截,使用拦截器 + AOP的模式实现。拦截器拦截下那些没有AdminOnly与Anonymous注解标注的方法请求,并进行用户认证。拦截器过完之后,去执行请求方法。AOP切AdminOnly注解的前置通知,植入一段管理员认证的切面逻辑。对Anonymous注解不进行任何处理,实现了匿名用户的访问。区别这样一看,拦截器就和AOP很像。那是因为我们这个例子还远没有发挥出AOP的实际价值。AOP比这个例子中看上去,强大得多。最近学习了设计模式中的代理模式,与AOP息息相关,我会在以后的文章中与大家一同学习。拦截器声明拦截器,第三个参数就是当前被拦截的方法。public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { HandlerMethod handlerMethod = (HandlerMethod) handler;}基本思路利用反射获取当前方法中是否标注有AdminOnly与Anonymous注解,如果没有,则进行普通用户认证。AdminOnly adminOnly = handlerMethod.getMethodAnnotation(AdminOnly.class);Anonymous anonymous = handlerMethod.getMethodAnnotation(Anonymous.class);if (adminOnly != null && anonymous != null) { return true;}boolean result = false;// 进行用户认证return result;性能优化反射每次请求,都要走拦截器,调用getMethodAnnotation方法。我们去看看getMethodAnnotation方法的源码实现:org.springframework.web.method.HandlerMethod中的getMethodAnnotation方法:@Nullablepublic <A extends Annotation> A getMethodAnnotation(Class<A> annotationType) { return AnnotatedElementUtils.findMergedAnnotation(this.method, annotationType);}该方法又调用了AnnotatedElementUtils.findMergedAnnotation方法,我们再点进去看看:org.springframework.core.annotation.AnnotatedElementUtils中的findMergedAnnotation实现:@Nullablepublic static <A extends Annotation> A findMergedAnnotation(AnnotatedElement element, Class<A> annotationType) { // Shortcut: directly present on the element, with no merging needed? A annotation = element.getDeclaredAnnotation(annotationType); if (annotation != null) { return AnnotationUtils.synthesizeAnnotation(annotation, element); } // Exhaustive retrieval of merged annotation attributes… AnnotationAttributes attributes = findMergedAnnotationAttributes(element, annotationType, false, false); return (attributes != null ? AnnotationUtils.synthesizeAnnotation(attributes, annotationType, element) : null);}该方法是调用AnnotatedElement接口中声明的getDeclaredAnnotation方法进行注解获取:AnnotatedElement接口,存在于java反射包中:话不多说,反射,就存在性能问题!个人理解同样是Java,我们看看Google对于Android反射的态度就好了。我记得之前我去过Google Android的官网,官方不推荐在Android中使用框架,这可能带来严重的性能问题,其中就有考虑到传统Java框架中大量使用的反射。这是国外一篇关于反射的文章,反射到底有多慢?:How Slow is Reflection in Android?文中提到了一项规范,即用户期待应用的启动时间的平均值为2s。NYTimes Android App中使用Google的Gson进行数据解析,这个在我们后台使用的还是挺广泛的,和阿里的fastjson齐名,都是非常火的json库。NYTimes的工程师发现Gson中使用反射来获取数据类型,导致应用启动时增加了大约700ms的延迟。ActiveAndroid是一个使用反射实现的库,特意去Github逛了一手,4000多star,这是相当流行的开源项目了!Scribd:1093ms for call com.activeandroid.ActiveAndroid.initialize。Myntra:1421ms for call com.activeandroid.ActiveAndroid.initialize。Data-Binding打脸?Android不是不推荐使用框架吗?那为什么Google又推出了Data-Binding呢?注意,Google考虑的是第三方框架高额的开销而引发性能问题。去看看Data-Binding的优点,最重要的一条就是该框架不使用反射,使用动态代码生成技术,不会因为使用该框架而造成性能问题。直接根据编写的代码生成原生Android的代码,所以不会存在任何性能问题!解决方案为了解决拦截器中使用反射的性能问题,我们学习SpringBoot的设计思路,在启动时直接完成所有反射注解的读取,存入内存。之后每次拦截器直接从内存中读取,提高性能。监听容器启动事件,在容器启动时执行以下代码,扫描所有控制器,及其方法上的注解,如果符合条件,则放到HashMap中。// 初始化组件扫描Scanner,禁用默认的filterClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);// 添加过滤条件,要求组件上有RestController注解scanner.addIncludeFilter(new AnnotationTypeFilter(RestController.class));// 在当前项目包下扫描所有符合条件的组件for (BeanDefinition beanDefinition : scanner.findCandidateComponents(basePackageName)) { // 获取当前组件的完整类名 String name = beanDefinition.getBeanClassName(); try { // 利用反射获取相关类 Class<?> clazz = Class.forName(name); // 初始化方法名List List<String> methodNameList = new ArrayList<>(); // 获取当前类(不包括父类,所以要求控制器间不能继承)中所有声明方法 for (Method method : clazz.getDeclaredMethods()) { // 获取方法上的注解 AdminOnly adminOnly = method.getAnnotation(AdminOnly.class); Anonymous anonymous = method.getAnnotation(Anonymous.class); // 如果该方法不存在AdminOnly和Anonymous注解 if (adminOnly == null && anonymous == null) { // 添加到List中 methodNameList.add(method.getName()); } } // 添加到Map中 AuthAnnotationConfig.getAnnotationsMap().put(clazz, methodNameList); } catch (ClassNotFoundException e) { logger.error(“扫描注解配置时,发生了ClassNotFoundException异常”); }}拦截器修改原来的拦截器是这样的:AdminOnly adminOnly = handlerMethod.getMethodAnnotation(AdminOnly.class);Anonymous anonymous = handlerMethod.getMethodAnnotation(Anonymous.class);if (adminOnly != null && anonymous != null) { return true;}boolean result = false;// 进行用户认证return result;现在是这样的:logger.debug(“获取当前请求方法的组件类型”);Class<?> clazz = handlerMethod.getBeanType();logger.debug(“获取当前处理请求的方法名”);String methodName = handlerMethod.getMethod().getName();logger.debug(“获取当前类中需认证的方法名”);List<String> authMethodNames = AuthAnnotationConfig.getAnnotationsMap().get(clazz);logger.debug(“如果List为空或者不包含在认证方法中,释放拦截”);if (authMethodNames == null || !authMethodNames.contains(methodName)) { return true;}logger.debug(“进行用户认证”);boolean result = false;// 用户认证return result;之前用了两次反射,现在是调用了handlerMethod.getBeanType()和handlerMethod.getMethod().getName()。再去看看这两个的实现:getBeanTypepublic Class<?> getBeanType() { return this.beanType;}getMethodpublic Method getMethod() { return this.method;}都是在org.springframework.web.method.HandlerMethod类中直接返回属性,我们推断:这个HandlerMethod,应该是Spring在容器启动时就已经构造好的方法对象,在拦截器执行期间,没有调用反射。注解的注解现在是注解少,我们写两行,感觉问题不大:// 获取方法上的注解AdminOnly adminOnly = method.getAnnotation(AdminOnly.class);Anonymous anonymous = method.getAnnotation(Anonymous.class);以后如果认证注解多了呢?我们期待这样,有一个通用的注解来判定当前方法是否要被拦截,而AdminOnly和Anonymous应继承该注解的功能,这样以后再想添加不被拦截器拦截的注解,就不需要修改启动时扫描的方法了。// 获取授权注解AdminAuth adminAuth = method.getAnnotation(AdminAuth.class);我们期望像Spring Boot一样,在注解上加注解,实现复合注解。@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@ControllerAdvice@ResponseBodypublic @interface RestControllerAdvice {}构造注解如果对Java自定义注解不了解,可以去慕课网学习相关课程:全面解析Java注解 - 慕课网@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})@Retention(RetentionPolicy.RUNTIME)public @interface AdminAuth {}@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE}),该注解可以标注在方法上,也可以标注在其他注解上。@Retention(RetentionPolicy.RUNTIME),该注解一直保留到程序运行期间。给注解加注解AdminOnly:@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)@AdminAuthpublic @interface AdminOnly {}Anonymous:@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)@AdminAuthpublic @interface Anonymous {}解析注解加注解很简单,重要的是怎么解析该注解。调用反射包中的Method类提供的getAnnotation方法,只会告诉我们当前标注了什么注解。比如:@AdminOnlypublic void test() {}我们可以通过getAnnotation获取AdminOnly,但是获取不到注解在@AdminOnly上的@AdminAuth注解。怎么获取注解的注解呢?找了一上午,不得不说,我解决这个问题还是靠一定的运气的。在我要放弃的时候,在Google搜出了SpringFramework中的注解工具类AnnotationUtils:随手打开文档:Class AnnotationUtils - Spring Core Docs第四个方法就是我想要的:使用该工具类,能直接获取方法上标注在注解上的注解:@AdminOnlypublic void test() {}AdminAuth adminAuth = AnnotationUtils.getAnnotation(method, AdminAuth.class);这种方法能获取到标注在test方法上继承而来的@AdminAuth注解。最终代码:@Componentpublic class InitAnnotationsConfig implements ApplicationListener<ContextRefreshedEvent> { // 基础包名 private static final String basePackageName = “com.mengyunzhi.checkApplyOnline”; // 日志 private static final Logger logger = LoggerFactory.getLogger(InitAnnotationsConfig.class); @Override public void onApplicationEvent(ContextRefreshedEvent event) { // 初始化组件扫描Scanner,禁用默认的filter ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false); // 添加过滤条件,要求组件上有RestController注解 scanner.addIncludeFilter(new AnnotationTypeFilter(RestController.class)); // 在当前项目包下扫描所有符合条件的组件 for (BeanDefinition beanDefinition : scanner.findCandidateComponents(basePackageName)) { // 获取当前组件的完整类名 String name = beanDefinition.getBeanClassName(); try { // 利用反射获取相关类 Class<?> clazz = Class.forName(name); // 初始化方法名List List<String> methodNameList = new ArrayList<>(); // 获取当前类(不包括父类,所以要求控制器间不能继承)中所有声明方法 for (Method method : clazz.getDeclaredMethods()) { // 获取授权注解 AdminAuth adminAuth = AnnotationUtils.getAnnotation(method, AdminAuth.class); // 如果该方法不被授权,则需要认证 if (adminAuth == null) { // 添加到List中 methodNameList.add(method.getName()); } } // 添加到Map中 AuthAnnotationConfig.getAnnotationsMap().put(clazz, methodNameList); } catch (ClassNotFoundException e) { logger.error(“扫描注解配置时,发生了ClassNotFoundException异常”); } } }}总结学会了一个解决问题的新办法:某个框架应该也遇到过你所遇到的问题,去找找框架中的工具类,这可能会很有帮助。 ...

January 23, 2019 · 2 min · jiezi