日期: 2021 年 7 月 9 日

记springboot配置ios通用链接

目录

一、什么是IOS通用链接:

二、Springboot配置apple-app-site-association文件

一、什么是IOS通用链接:
在app开发中我们经常需要从浏览器,Safari中去唤醒app,iOS 9之前我们通过配置scheme从而实现这种跳转,但是这种方式需要提前判断系统中是否安装了能够响应对应scheme的app,这种方式在微信中是被禁用的。Universal Links是iOS 9推出的一项功能,我们可以通过配置Universal Links使用户能够通过一个链接进入app或者指定页面,这个不会被微信禁用。

IOS官方文档地址:https://developer.apple.com/library/archive/documentation/General/Conceptual/AppSearch/UniversalLinks.html#//apple_ref/doc/uid/TP40016308-CH12-SW1

IOS端参考配置地址:https://www.jianshu.com/p/0c2af36aa6ac

二、Springboot配置apple-app-site-association文件
根据官方文档要求,仅需要在根目录下配置该文件,就可以交由ios跳转的进行校验。

项目环境:

1. springboot 2.2.3.RELEASE

2. 该项目是jar包运行,估没有webapp,但有static文件夹

%title插图%num

%title插图%num

 

配置完毕,直接访问:localhost/apple-app-site-association,即完成配置

demo地址:https://gitee.com/banbeisudashui/ios_apple_demo

SpringBoot Java后端校验Sign in With Apple (苹果授权登录)

先吐槽一下!苹果的开发文档简直就是一坨S

好了!!开发步骤如下:
1.导入JWT Maven 坐标

<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.0</version>
</dependency>

2.闭上眼睛,以GET方式不带参数访问 https://appleid.apple.com/auth/keys。获取*新JSON,对我们有用的就是”n”和”e”,组成JWT公钥(此处官方有变更,现在获取到的keys数组为多个,需要根据下面第三步IOS端获取的identityToken中的kid来找对应下标的keys数组)。

获取到的json内容:
{
“keys”: [
{
“kty”: “RSA”,
“kid”: “AIDOPK1”,
“use”: “sig”,
“alg”: “RS256”,
“n”: “(可能每次都不一样)xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx”,
“e”: “AQAB”
}
]
}

ps.我这里使用的是SpringBoot RestTemplate的GET请求方式,然后根据上面的”n”和”e”组成公钥:

AUTH_DOMAIN = https://appleid.apple.com/auth/keys
AppleKeys:请求返回数据的对象
RSA = RSA
public PublicKey getPublicKey() {
try {
String forObject = restTemplate.getForObject(AUTH_DOMAIN, String.class);
if(StringUtils.isEmpty(forObject)){ return null; }
AppleKeys appleKeys = JsonUtils.parseObject(forObject, AppleKeys.class);
List<AppleKeys.Keys> keys = appleKeys.getKeys();
String n = keys.get(0).getN();
String e = keys.get(0).getE();
final BigInteger modulus = new BigInteger(1, Base64.decodeBase64(n));
final BigInteger publicExponent = new BigInteger(1, Base64.decodeBase64(e));
final RSAPublicKeySpec spec = new RSAPublicKeySpec(modulus, publicExponent);
final KeyFactory kf = KeyFactory.getInstance(RSA);
return kf.generatePublic(spec);
} catch (final Exception e) {
log.debug(“[APPLE]ERROR:{}”,e)
}
return null;
}

3.IOS客户端经过一系列操作获取到:identityToken,userID等。将identityToken传递到后台(其他参数没啥用)

4.后台取到IOS客户端传过来的 identityToken 是jwt格式(固定三段式以“.”切割…我们只需要取第二段即可)。然后使用Base64解析出来,里面对我们有用的数据只有三个字段:

“iss”=https://appleid.apple.com(固定签名)(**此字段已被官方替换成 “kid” 用于找到第二步中kid匹配的keys**);
“aud”=APPID
“sub”=**用户的唯一标识**(所以前端不需要额外传递userID)

代码:

String [] identityTokens = identityToken.split(“\\.”);
Map<String, Object> data = JsonUtils.parseMap(new String(Base64.decodeBase64(identityTokens[1]),”UTF-8″),String.class, Object.class);
String iss = (String) data.get(“kid”);
String aud = (String) data.get(“aud”);
String sub = (String) data.get(“sub”);

5.将”identityToken”和上一步解析出来的”kid”,“aud”,”sub”传入一下方法验证JWT是否有效,返回true即等于没问题:

AUTH_TIME_STR = auth_time
public boolean verify(String identityToken,String iss,String aud,String sub) {
PublicKey publicKey = getPublicKey();
JwtParser jwtParser = Jwts.parser().setSigningKey(publicKey);
jwtParser.requireIssuer(iss);
jwtParser.requireAudience(aud);
jwtParser.requireSubject(sub);
try {
Jws<Claims> claim = jwtParser.parseClaimsJws(identityToken);
if (null != claim && claim.getBody().containsKey(AUTH_TIME_STR)) {
return true;
}
return false;
} catch (ExpiredJwtException e) {
return false;
} catch (Exception e) {
return false;
}
}

6.没问题的话将userID保存数据库(本文数据库操作忽略…)

使用Apple第三方登录Java版

由IOS端传过来code

服务端代码

import java.security.KeyFactory;

import java.security.PrivateKey;

import java.security.spec.PKCS8EncodedKeySpec;

import java.util.Base64;

import java.util.Collections;

import java.util.HashMap;

import java.util.List;

import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.web.bind.annotation.GetMapping;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RestController;

import com.alibaba.fastjson.JSONObject;

import com.auth0.jwt.JWT;

import com.auth0.jwt.interfaces.DecodedJWT;

import io.jsonwebtoken.Jwts;

import io.jsonwebtoken.SignatureAlgorithm;

public class AppleLoginController {

/**

* 苹果授权登录

*

* @param code

* @param subject

* @return

* @throws Exception

*/

@GetMapping(“/authCode”)

public void authCode(String code) throws Exception {
String client_id = “com.**.**”; // 被授权的APP ID

Map<String, Object> header = new HashMap<String, Object>();

header.put(“kid”, “***”); // 参考后台配置

Map<String, Object> claims = new HashMap<String, Object>();

claims.put(“iss”, “***”); // 参考后台配置 team id

long now = System.currentTimeMillis() / 1000;

claims.put(“iat”, now);

claims.put(“exp”, now + 86400 * 30); // *长半年,单位秒

claims.put(“aud”, “https://appleid.apple.com”); // 默认值

claims.put(“sub”, client_id);

PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(readKey());

KeyFactory keyFactory = KeyFactory.getInstance(“EC”);

PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);

String client_secret = Jwts.builder().setHeader(header).setClaims(claims)

.signWith(SignatureAlgorithm.ES256, privateKey).compact();

String url = “https://appleid.apple.com/auth/token”;

// POST 请求

Map<String, String> params = new HashMap<String, String>();

Map<String, String> headers = new HashMap<String, String>();

headers.put(“Content-Type”, “application/x-www-form-urlencoded”);

params.put(“client_id”, client_id);

params.put(“client_secret”, client_secret);

params.put(“code”, code);

params.put(“grant_type”, “authorization_code”);

params.put(“redirect_uri”, “”);

HttpClientResult httpClientResult = HttpClientUtil.doPost(url, headers, params);

Map<String, Object> jsonObject = JSONObject.parseObject(httpClientResult.getContent());

DecodedJWT jwtString = JWT.decode(jsonObject.get(“id_token”).toString());

String sub = jwtString.getSubject();

}

private byte[] readKey() throws Exception {
String temp = “*******”;//记得去掉换行符号,密钥

return Base64.getDecoder().decode(temp);

}

}

jar包依赖

<dependency>

<groupId>io.jsonwebtoken</groupId>

<artifactId>jjwt</artifactId>

<version>0.7.0</version>

</dependency>

<!– https://mvnrepository.com/artifact/com.auth0/java-jwt –>

<dependency>

<groupId>com.auth0</groupId>

<artifactId>java-jwt</artifactId>

<version>3.10.3</version>

</dependency>

苹果授权登录(Sign in with Apple)-JAVA后端开发

1、 业务逻辑分析
IOS授权登录流程与微信授权登录大同小异,唯一区别的在于需要调用苹果api获取公钥,接口地址为:https://appleid.apple.com/auth/keys。
首先是IOS APP端拿到identifyToken交给后端,后端拿到identifyToken后,首先调用IOS的公钥API拿到IOS的公钥,这里会获取到两个公钥,然后使用公钥对identifyToken进行校验,校验通过后,对identityToken进行解码,解码后可以到授权的唯一标识sub,之后做业务侧的注册登录逻辑。
这里有一个坑,就是在校验identifyToken的时候,偶尔会校验不通过,原因是我们通过IOS的公钥API会拿到两个密钥,如果只拿其中一个去做校验,就会出现这种情况,所以当*个密钥校验不通过的时候,再拿第二个密钥再做一次校验。

大致流程如下图:

%title插图%num
代码实现
需要添加的依赖:

<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<dependency>
<groupId>com.auth0</groupId>
<artifactId>jwks-rsa</artifactId>
<version>0.9.0</version>
</dependency>

工具类实现

public class AppleUtil {

private static final Logger logger = LoggerFactory.getLogger(AppleUtil.class);

/**
* 获取苹果的公钥
* @return
* @throws Exception
*/
private static JSONArray getAuthKeys() throws Exception {
String url = “https://appleid.apple.com/auth/keys”;
RestTemplate restTemplate = new RestTemplate();
JSONObject json = restTemplate.getForObject(url,JSONObject.class);
JSONArray arr = json.getJSONArray(“keys”);
return arr;
}

public static Boolean verify(String jwt) throws Exception{
JSONArray arr = getAuthKeys();
if(arr == null){
return false;
}
JSONObject authKey = null;

//先取苹果*个key进行校验
authKey = JSONObject.parseObject(arr.getString(0));
if(verifyExc(jwt, authKey)){
return true;
}else{
//再取第二个key校验
authKey = JSONObject.parseObject(arr.getString(1));
return verifyExc(jwt, authKey);
}

}

/**
* 对前端传来的identityToken进行验证
* @param jwt 对应前端传来的 identityToken
* @param authKey 苹果的公钥 authKey
* @return
* @throws Exception
*/
public static Boolean verifyExc(String jwt, JSONObject authKey) throws Exception {

Jwk jwa = Jwk.fromValues(authKey);
PublicKey publicKey = jwa.getPublicKey();

String aud = “”;
String sub = “”;
if (jwt.split(“\\.”).length > 1) {
String claim = new String(Base64.decodeBase64(jwt.split(“\\.”)[1]));
aud = JSONObject.parseObject(claim).get(“aud”).toString();
sub = JSONObject.parseObject(claim).get(“sub”).toString();
}
JwtParser jwtParser = Jwts.parser().setSigningKey(publicKey);
jwtParser.requireIssuer(“https://appleid.apple.com”);
jwtParser.requireAudience(aud);
jwtParser.requireSubject(sub);

try {
Jws<Claims> claim = jwtParser.parseClaimsJws(jwt);
if (claim != null && claim.getBody().containsKey(“auth_time”)) {
System.out.println(claim);
return true;
}
return false;
} catch (ExpiredJwtException e) {
logger.error(“apple identityToken expired”, e);
return false;
} catch (Exception e) {
logger.error(“apple identityToken illegal”, e);
return false;
}
}

/**
* 对前端传来的JWT字符串identityToken的第二部分进行解码
* 主要获取其中的aud和sub,aud大概对应ios前端的包名,sub大概对应当前用户的授权的openID
* @param identityToken
* @return {“aud”:”com.xkj.****”,”sub”:”000***.8da764d3f9e34d2183e8da08a1057***.0***”,”c_hash”:”UsKAuEoI-****”,”email_verified”:”true”,”auth_time”:1574673481,”iss”:”https://appleid.apple.com”,”exp”:1574674081,”iat”:1574673481,”email”:”****@qq.com”}
*/
public static JSONObject parserIdentityToken(String identityToken){
String[] arr = identityToken.split(“\\.”);
Base64 base64 = new Base64();
String decode = new String (base64.decodeBase64(arr[1]));
String substring = decode.substring(0, decode.indexOf(“}”)+1);
JSONObject jsonObject = JSON.parseObject(substring);
return jsonObject;
}

}

业务侧注册登录,具体的注册登录逻辑这里不再详细介绍

/**
* iOS appleid 授权登录
* @param identityToken
* @param request
* @return
*/
@Transactional
public ApiResult appleLogin(String identityToken,HttpServletRequest request){
try {
Map<String, String> map = new HashMap<String, String>();
//验证identityToken
if(!AppleUtil.verify(identityToken)){
return new ApiResult(ApiCode.VALIDATION_ERROR, “授权验证失败”);
}
//对identityToken解码
JSONObject json = AppleUtil.parserIdentityToken(identityToken);
if(json == null){
return new ApiResult(ApiCode.VALIDATION_ERROR, “授权解码失败”);
}
String ip = Servlets.getRemoteAddr(request);
User user = oauthAppleService.apple_login(json, ip);
//将用户信息存到redis并返回token, token有效期为31天
String token = tokenRedisService.put(user);
map.put(“token”,token);
return new ApiResult(ApiCode.SUCCESS, “success”, map);
}catch (Exception e){
logger.error(“app wxLogin error:” + e.getMessage(),e);
return new ApiResult(ApiCode.SYS_EXCEPTION, “系统错误”);
}

}

苹果APP授权登录

一、前置说明
苹果App开发完成后支持微信QQ一键登录,审核时要求具备Apple登录以防万一,苹果登录有两种方式,一种是jwt,二是授权码模式,这里采用jwt模式,开发语言为java

二、代码实现
使用相关技术:jwt+restTemplate+fastjson

/**
* 解析apple授权token
* @param jwt
* @return
* @throws Exception
*/
@Override
public JSONObject appleAuth(String jwt) throws Exception {
//解析identityToken
String[] jwtArray = jwt.split(“\\.”);
String claim = new String(Base64.decodeBase64(jwtArray[1]));
JSONObject claimJson = JSONObject.parseObject(claim);
// 解析头部 kid
String header = new String(Base64.decodeBase64(jwtArray[0]));
JSONObject headerJson = JSONObject.parseObject(header);
// 请求获取苹果公钥
String body = restTemplate.getForObject(“https://appleid.apple.com/auth/keys”,String.class);
JSONObject jsonObject = JSONObject.parseObject(body);
String keys = jsonObject.getString(“keys”);
JSONArray arr = JSONObject.parseArray(keys);
JSONObject appleKey1 = JSONObject.parseObject(arr.getString(0));
JSONObject appleKey2 = JSONObject.parseObject(arr.getString(1));
JSONObject appleKey = appleKey1;
if( !headerJson.getString(“kid”).equals(appleKey1.getString(“kid”)) ){
appleKey = appleKey2;
}
// 生成苹果公钥
BigInteger modulus = new BigInteger(1, Base64.decodeBase64(appleKey.getString(“n”)));
BigInteger publicExponent = new BigInteger(1, Base64.decodeBase64(appleKey.getString(“e”)));
RSAPublicKeySpec rsaPublicKeySpec = new RSAPublicKeySpec(modulus, publicExponent);

//生成 PublicKey
KeyFactory kf = KeyFactory.getInstance(“RSA”);
PublicKey publicKey = kf.generatePublic(rsaPublicKeySpec);
//校验有效
return verify(publicKey,jwt,claimJson);
}

public JSONObject verify(PublicKey key, String jwt, JSONObject claimJson){
//构造返回数据
JSONObject result = new JSONObject();
boolean status = false;
//校验
JwtParser jwtParser = Jwts.parser().setSigningKey(key);
jwtParser.requireIssuer(claimJson.getString(“iss”))
.requireAudience(claimJson.getString(“aud”))
.requireSubject(claimJson.getString(“sub”));
try {
Jws<Claims> claim = jwtParser.parseClaimsJws(jwt);
if (claim != null && claim.getBody().containsKey(“auth_time”)) {
//校验成功
status = true;
//返回 用户的唯一标识
result.put(“sub”,claimJson.getString(“sub”));
}
}catch (Exception e) {
log.error(“校验失败”);
}
result.put(“status”,status);
return result;
}

苹果CMS内容管理系统 – 苹果CMS官方网站

自从苹果cms官方网站关闭后,对于刚接触的小伙伴们就已经懵逼状态了!今天我在这给大家普及下。

我们可以用百度搜索 “苹果cms” 会出现下面的页面,排在*位的就是苹果cms官网,不过现在是关站状态。具体原因下面讲。

%title插图%num

下面的几个地址是有关于苹果cms的:

①www.maccms.com/官网(现在是关闭状态)

②http://t.maccms.com/(网站关停页面)在本页面按下—-F12 会有彩蛋出现

③https://github.com/magicblack这个是苹果cms在github平台的地址,也是现在唯一更新的地址。如果你看不懂英文请用浏览器翻译下。请牢记

⑤https://www.mytheme.cn/这个是苹果cms的模板商 专业制作苹果cms模板 此行业零头衔。

④https://www.showdoc.cc/mytheme?page_id=2779989134071820这个是关于苹果cms的一些入门级使用教程。

关于苹果cms为什么会关闭,官方给出的答案是:

由于不可抗因素已于19年3月关闭,预计20年5月恢复

老王原QQ暂时无法使用,请勿联系请勿发任何信息

苹果CMS软件著作权证书号:2018SR947830,请勿非法修改发布。

cmd中检测远程的ip和端口是否处于监听状态

*步:使用ping命令测试远程的ip是否可连通

在cmd中输入ping ip地址:

%title插图%num

如果*后的统计信息中丢失率为0表示可以连通该ip地址。那么就可以进行下一步的测试该ip下的某一个端口是否开放的操作了。

第二步:使用telnet测试远程某一个ip的端口是否开放
为了安全起见,windows默认是不开启telnet客户端的,要通过控制面板—》程序–》启动或关闭windows功能—》telnet客户端—》确定

%title插图%num

安装完成telnet客户端之后,就可以在cmd中使用telnet命令了。
使用命令:telnet ip地址 端口号

如果出现下面的提示说明连接失败,端口号是未开启的,或者没有处于监听状态

%title插图%num

如果出现下面的空白界面说明连接成功,端口号是开启,并处于监听状态的

%title插图%num

 

在cmd检测端口和服务器的方法

点击左下角在输入框输入cmd,回车:
1.ping ip; 检测服务地址是否相通

2.telnet ip 端口号;检测端口号是否打开

3.netstat -anp tcp;查看当前主机tcp开放的端口

4.ipconfig 查看当前的服务器ip地址

linux系统下:右键选择 打开终端:

输入ifconfig 查看当前服务器ip地址

/etc/init.d/sshd start 打开虚拟机上的22端口

在cmd窗口下连接服务端

在cmd用MySQL连接服务端几种方式
一般来说,我们都会用到数据库做一些数据的增删查改。假如,我们在已知用户和密码的情况下连接数据库,用户名为“root”,密码“123456”。

一、连接本地数据库

C:\WINDOWS\system32>mysql -h localhost -u root -p123456

注意:“-p”和“123456” 之间不能有空格
1
二、连接本地数据库内指定的数据库,数据库名为“db”

C:\WINDOWS\system32>mysql -h 127.0.0.1 -u root -p123456 -D db

三、MySQL 连接本地数据库,可以先不输入密码,然后根据提示输入密码:

C:\WINDOWS\system32> mysql -h localhost -u root -p
Enter password:
%title插图%num

出现以上提示则输入密码后回车即可。

四、MySQL 连接远程数据库(192.168.1.10),端口“3306”,用户名为“root”,密码“123456”

C:\WINDOWS\system32>mysql -h 192.168.1.10 -P 3306 -u root -p123456

注意:这里是大写的“P”表示指明端口号。
1
我运行的cmd窗口都使用了管理员模式,密码一般为初始密码,不排除有自己修改过的可能性,所以这里的密码仅供参考。

231. 2的幂(JS实现)

231. 2的幂(JS实现)
1 题目
给定一个整数,编写一个函数来判断它是否是 2 的幂次方。
示例 1:
输入: 1
输出: true
解释: 20 = 1
示例 2:
输入: 16
输出: true
解释: 24 = 16
示例 3:
输入: 218
输出: false
链接:https://leetcode-cn.com/problems/power-of-two
2 思路
这道题用位运算*快,满足2的幂,则二进制表达只有一个1,其余全为0
3代码
/**
 * @param {number} n
 * @return {boolean}
 */
var isPowerOfTwo = function(n) {
  if (n <= 0) return false;
  return n & (n – 1) == 0;
};
友情链接: SITEMAP | 旋风加速器官网 | 旋风软件中心 | textarea | 黑洞加速器 | jiaohess | 老王加速器 | 烧饼哥加速器 | 小蓝鸟 | tiktok加速器 | 旋风加速度器 | 旋风加速 | quickq加速器 | 飞驰加速器 | 飞鸟加速器 | 狗急加速器 | hammer加速器 | trafficace | 原子加速器 | 葫芦加速器 | 麦旋风 | 油管加速器 | anycastly | INS加速器 | INS加速器免费版 | 免费vqn加速外网 | 旋风加速器 | 快橙加速器 | 啊哈加速器 | 迷雾通 | 优途加速器 | 海外播 | 坚果加速器 | 海外vqn加速 | 蘑菇加速器 | 毛豆加速器 | 接码平台 | 接码S | 西柚加速器 | 快柠檬加速器 | 黑洞加速 | falemon | 快橙加速器 | anycast加速器 | ibaidu | moneytreeblog | 坚果加速器 | 派币加速器 | 飞鸟加速器 | 毛豆APP | PIKPAK | 安卓vqn免费 | 一元机场加速器 | 一元机场 | 老王加速器 | 黑洞加速器 | 白石山 | 小牛加速器 | 黑洞加速 | 迷雾通官网 | 迷雾通 | 迷雾通加速器 | 十大免费加速神器 | 猎豹加速器 | 蚂蚁加速器 | 坚果加速器 | 黑洞加速 | 银河加速器 | 猎豹加速器 | 海鸥加速器 | 芒果加速器 | 小牛加速器 | 极光加速器 | 黑洞加速 | movabletype中文网 | 猎豹加速器官网 | 烧饼哥加速器官网 | 旋风加速器度器 | 哔咔漫画 | PicACG | 雷霆加速