Token(計算機術語)
在計算機身份認證中是令牌(臨時)的意思,在詞法分析中是標記的意思。
數據處理
token其實說的更通俗點可以叫暗號,在一些數據傳輸之前,要先進行暗號的核對,不同的暗號被授權不同的數據操作。例如在USB1.1協議中定義了4類數據包:token包、data包、handshake包和special包。主機和USB設備之間連續數據的交換可以分為三個階段,第一個階段由主機發送token包,不同的token包內容不一樣(暗號不一樣)可以告訴設備做不同的工作,第二個階段發送data包,第三個階段由設備返回一個handshake包。
**(網絡上)基于 Token 的身份驗證方法 **
使用基于 Token 的身份驗證方法,在服務端不需要存儲用戶的登錄記錄。大概的流程是這樣的
- 客戶端使用用戶名跟密碼請求登錄
- 服務端收到請求,去驗證用戶名與密碼
- 驗證成功后,服務端會簽發一個 Token,再把這個 Token 發送給客戶端 -
- 客戶端收到 Token 以后可以把它存儲起來,比如放在 Cookie 里或者 Local Storage 里
- 客戶端每次向服務端請求資源的時候需要帶著服務端簽發的 Token
- 服務端收到請求,然后去驗證客戶端請求里面帶著的 Token,如果驗證成功,就向客戶端返回請求的數據
** 下面是個人的理解: **
1.登錄的時候(或者第一次打開APP的時候),登錄成功之后,我們需要給他分配一個token。(1)token可以是文件的形式存著;(2)也可以存在數據庫,但是存放在數據庫,我個人不推薦,因為每次調用api接口都會對比,這樣做法會加重服務器壓力;(3)用redis存放token。
2.登錄之后,我們要返回token,讓安卓或者ios去保存這個token,以后每次操作都攜帶token去請求接口。
3.接下來就是我們要用它傳給我們的token去對比,如果符合,那就正常返回數據,否則就返回一個標識,告訴app說這個token不符合,需要重新登錄。
下面我們來看一下token的具體使用
在開發中我的需求是用戶第一次打開我們的APP的時候就獲取到一個token,然后保存到本地,這樣在下一次打開后我們就能根據token來判斷用戶的信息(如果用戶注冊,則把用戶信息和token在后臺綁定)
** 1:首先獲取token(這里是當打開APP的時候的一個Activity中)**
//初始化數據,獲得應用的token并且保存
public void initData () {
//判斷有沒有舊的token,AndroidFileUtil這個工具類在下面的代碼中
String myToken = AndroidFileUtil.readFileByLines(getCacheDir().getAbsolutePath() + "/" + DataConfig.TOKEN_FILE_NAME);
if (!TextUtils.isEmpty(myToken)) {
Log.d("WelcomeActivity", "Token: " + myToken);
} else {
APIConfig.getDataIntoView(new Runnable() {
@Override
public void run() {
String member = "member";
Map<String, String> map = new HashMap<>();
map.put("grantType", member);
map.put("token", "");
map.put("appId", "");
map.put("appSecret", "");
//對傳入的數據進行加密
String paramJson = EncryptUtil.encrypt(map);
//下面的是獲取token的服務器地址,項目中應該根據具體的請求地址
String url = "http://47.96.175.241/shop/api/token/refresh.do";
String rs = HttpUtil.GetDataFromNetByPost(url,
new ParamsBuilder().addParam("paramJson", paramJson).getParams());
//對數據進行解密到我們的一個保存token的類中(UserToken類)
final UserToken result = EncryptUtil.decrypt(rs, UserToken.class);
if (result != null && result.getResult() == APIConfig.CODE_SUCCESS) {
//保存我們獲取的token在文件中,方便下次獲取,這個工具也在下面
APIUtil.saveToken(result.getData());
} else {
//下面的是自己寫的一個工具類,也可以用Toast彈窗消息
ToastUtil.toastByCode(result);
}
}
});
}
}
** 加密解密的類(由于是在網絡上傳送數據,安全性很重要)**
public class EncryptUtil {
private static final String ALGORITHM = "AES/ECB/PKCS5Padding";
// 加密秘鑰
private static final String AES_KEY = "Super-winner/168";
private static SecretKeySpec secretKeySpec;
/**
* 前臺傳輸數據解密
*
* @param rawJson 原始JSON
* @return 解密后的Map
*/
//其中的BaseResult也在下面
public static <T extends BaseResult> T decrypt(String rawJson, Class<T> tClass) {
T result = null;
try {
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, getAesKey());
byte[] paramBytes = cipher.doFinal(Base64.decode(rawJson.getBytes("UTF-8"), Base64.NO_WRAP));
String paramJson = new String(paramBytes);
result = GsonUtil.fromJson(paramJson, tClass);
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return result;
}
/**
* 數據傳輸過程中需要加密設置
* @param rawMap
* @return
*/
public static String encrypt(Map<String, String> rawMap) {
String result = "";
try {
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, getAesKey());
String rawJson = GsonUtil.toJson(rawMap);
byte[] paramBytes = cipher.doFinal(rawJson.getBytes("UTF-8"));
result = Base64.encodeToString(paramBytes, Base64.NO_WRAP);
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return result;
}
private static SecretKeySpec getAesKey() {
if (secretKeySpec != null) {
return secretKeySpec;
}
try {
secretKeySpec = new SecretKeySpec(AES_KEY.getBytes("UTF-8"), "AES");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return secretKeySpec;
}
}
** 參數構造類 **
public class ParamsBuilder {
private HashMap<String, String> params;
public ParamsBuilder() {
}
public ParamsBuilder addParam(String key, String value) {
if (params == null) {
params = new HashMap<>();
}
params.put(key, value);
return this;
}
public HashMap<String, String> getParams() {
return params;
}
}
** 保存token的類 **
/**
* 打開應用就獲取到token
* Created by acer-pc on 2018/7/26.
*/
public class UserToken extends BaseResult {
private String data;
public String getData() {
return data;
}
public void setData(String data) {
this.data = data;
}
}
** 保存token的工具類 **
public class APIUtil {
public interface CallBack<T extends BaseResult> {
void handleResult(T result);
}
/**
* 保存用戶打開APP的時候的token信息
* @param token 用戶
*/
public static void saveToken(String token) {
RuntimeConfig.token = token;
String absolutePath = WinnerApplication.getContext().getCacheDir().getAbsolutePath();
AndroidFileUtil.writeStringToFile("true", absolutePath, DataConfig.PUSH_FILE_NAME);
AndroidFileUtil.writeStringToFile(token, absolutePath, DataConfig.TOKEN_FILE_NAME);
RuntimeConfig.FIRST_STARTUP = true;
}
}
** 寫入到文件中的一個工具類 **
/**
* Created by acer-pc on 2018/7/26.
*/
public class AndroidFileUtil {
/**
* 存儲登錄token
*
* @param string
* @param filePath
* @param fileName
*/
public static void writeStringToFile(String string, String filePath, String fileName) {
BufferedOutputStream bos = null;
FileOutputStream fos = null;
File file;
try {
file = new File(filePath, fileName);
if (file.exists()) {
boolean delete = file.delete();
LogUtil.i("刪除文件", delete + "");
}
File dir = new File(filePath);
if (!dir.exists() && !dir.isDirectory()) {//判斷文件夾目錄是否存在
boolean mkdirs = dir.mkdirs();
LogUtil.i("創建文件夾", mkdirs + "");
}
LogUtil.i("token write file path", file.getAbsolutePath());
fos = new FileOutputStream(file);
bos = new BufferedOutputStream(fos);
bos.write(string.getBytes());
} catch (Exception e) {
e.printStackTrace();
} finally {
if (bos != null) {
try {
bos.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
if (fos != null) {
try {
fos.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
}
public static void deleteFile(File file) {
if (file.exists()) {
boolean delete = file.delete();
LogUtil.i("刪除文件", delete + "");
}
}
/**
* 刪除token
*
* @param filePath
* @param fileName
*/
private static void deleteToken(File filePath, String fileName) {
File file = new File(filePath, fileName);
if (file.exists()) {
boolean delete = file.delete();
LogUtil.i("刪除token", delete + "");
if (delete) {
String absolutePath = WinnerApplication.getContext().getCacheDir().getAbsolutePath();
AndroidFileUtil.writeStringToFile("true", absolutePath, DataConfig.PUSH_FILE_NAME);
}
}
}
public static void deleteToken() {
deleteToken(new File(WinnerApplication.getContext().getCacheDir().getAbsolutePath() + "/"), DataConfig.TOKEN_FILE_NAME);
}
/**
* 讀取token
*
* @param fileName
* @return
*/
public static String readFileByLines(String fileName) {
LogUtil.i("token read file path: ", fileName);
File file = new File(fileName);
BufferedReader reader = null;
StringBuffer sb = new StringBuffer();
try {
if (!file.exists()) {
return "";
}
reader = new BufferedReader(new FileReader(file));
String tempString = null;
while ((tempString = reader.readLine()) != null) {
sb.append(tempString);
}
reader.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
return sb.toString();
}
/**
* <br>功能簡述:4.4及以上獲取圖片的方法
* <br>功能詳細描述:
* <br>注意:
*
* @param context
* @param uri
* @return
*/
@TargetApi(Build.VERSION_CODES.KITKAT)
public static String getPath(final Context context, final Uri uri) {
final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
// DocumentProvider
if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
// ExternalStorageProvider
if (isExternalStorageDocument(uri)) {
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[0];
if ("primary".equalsIgnoreCase(type)) {
return Environment.getExternalStorageDirectory() + "/" + split[1];
}
}
// DownloadsProvider
else if (isDownloadsDocument(uri)) {
final String id = DocumentsContract.getDocumentId(uri);
final Uri contentUri = ContentUris.withAppendedId(
Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
return getDataColumn(context, contentUri, null, null);
}
// MediaProvider
else if (isMediaDocument(uri)) {
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[0];
Uri contentUri = null;
if ("image".equals(type)) {
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
} else if ("video".equals(type)) {
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
} else if ("audio".equals(type)) {
contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
}
final String selection = "_id=?";
final String[] selectionArgs = new String[]{split[1]};
return getDataColumn(context, contentUri, selection, selectionArgs);
}
}
// MediaStore (and general)
else if ("content".equalsIgnoreCase(uri.getScheme())) {
// Return the remote address
if (isGooglePhotosUri(uri))
return uri.getLastPathSegment();
return getDataColumn(context, uri, null, null);
}
// File
else if ("file".equalsIgnoreCase(uri.getScheme())) {
return uri.getPath();
}
return null;
}
public static String getDataColumn(Context context, Uri uri, String selection,
String[] selectionArgs) {
Cursor cursor = null;
final String column = "_data";
final String[] projection = {column};
try {
cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null);
if (cursor != null && cursor.moveToFirst()) {
final int index = cursor.getColumnIndexOrThrow(column);
return cursor.getString(index);
}
} catch (Exception ex) {
ex.printStackTrace();
} finally {
if (cursor != null)
cursor.close();
}
return null;
}
/**
* @param uri The Uri to check.
* @return Whether the Uri authority is ExternalStorageProvider.
*/
public static boolean isExternalStorageDocument(Uri uri) {
return "com.android.externalstorage.documents".equals(uri.getAuthority());
}
/**
* @param uri The Uri to check.
* @return Whether the Uri authority is DownloadsProvider.
*/
public static boolean isDownloadsDocument(Uri uri) {
return "com.android.providers.downloads.documents".equals(uri.getAuthority());
}
/**
* @param uri The Uri to check.
* @return Whether the Uri authority is MediaProvider.
*/
public static boolean isMediaDocument(Uri uri) {
return "com.android.providers.media.documents".equals(uri.getAuthority());
}
/**
* @param uri The Uri to check.
* @return Whether the Uri authority is Google Photos.
*/
public static boolean isGooglePhotosUri(Uri uri) {
return "com.google.android.apps.photos.content".equals(uri.getAuthority());
}
}
** 其中的BaseResult **
**
* Created by acer-pc on 2018/6/19.
*/
public class BaseResult {
private int result;
private String message;
public int getResult() {
return result;
}
public void setResult(int result) {
this.result = result;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}