关于客户端搭建:【java】基于OkHttpClient实现get和post请求接口/客户端【postman测试wireshark抓包】
token鉴权:
使用用户名和密码去请求,通过算法生成token,但是token不存储在服务器,随后直接把token返回给客户端。一个token分为三个部分,第一部分是Header,第二部分是用户相关信息,第三个部分是签名,相当于一把锁,前两个部分通过HMAC-SHA256算法生成一把钥匙。
需要判断钥匙和锁是否匹配,但是不会直接拿密钥和签名对比,而是重新计算出一个签名(锁),比较两把锁是否相同。数据(Header,用户信息)是一致的,算法也没有变化,结果就不会变,如果数据变化(被篡改)就不会验证通过。
Token和session的区别:
Token其实也是一个字符串,它最大的一个特点就是不会存在服务端,还有一个特点就是不再依赖于cookie的形式,它可以是请求头或者是请求体的方式,也就是Session依赖于cookie,由服务器生成,存储,验证,以cookie的方式存在于客户端,客户端以同样方式发送给服务端,session有状态。
H. 怎么使用token或者session做鉴权做鉴权?
1登陆之后,获取session或者token字符串,将它们携带回客户端。
2请求需要鉴权的接口时,携带对应的session和token。
JWT的身份验证:
使用基于 Token 的身份验证方法,在服务端不需要存储用户的登录记录。大概的流程是这样的:
1、客户端使用用户名跟密码请求登录
2、服务端收到请求,去验证用户名与密码
3、验证成功后,服务端会签发一个 Token,再把这个 Token 发送给客户端
4、客户端收到 Token 以后可以把它存储起来,比如放在 Cookie 里或者 Local Storage 里
5、客户端每次向服务端请求资源的时候需要带着服务端签发的 Token
6、服务端收到请求,然后去验证客户端请求里面带着的 Token,如果验证成功,就向客户端返回请求的数据
JWT的token组成
实施 Token 验证的方法挺多的,还有一些标准方法,比如 JWT,读作:jot ,表示:JSON Web Tokens 。JWT 标准的 Token 有三个部分:
header(头部)
payload(数据)
signature(签名)
中间用点分隔开,并且都会使用 Base64 编码
头部:
每个 JWT token 里面都有一个 header,也就是头部数据。里面包含了使用的算法,这个 JWT 是不是带签名的或者加密的。主要就是说明一下怎么处理这个 JWT token 。
头部里包含的东西可能会根据 JWT 的类型有所变化,比如一个加密的 JWT 里面要包含使用的加密的算法。唯一在头部里面要包含的是 alg 这个属性,如果是加密的 JWT,这个属性的值就是使用的签名或者解密用的算法。如果是未加密的 JWT,这个属性的值要设置成 none。
算法是 HS256:上面的内容得用 base64url 的形式编码
Payload:
Payload 里面是 Token 的具体内容,这些内容里面有一些是标准字段,你也可以添加其它需要的内容。下面是标准字段:
iss:Issuer,发行者
sub:Subject,主题
aud:Audience,观众
exp:Expiration time,过期时间
nbf:Not before
iat:Issued at,发行时间
jti:JWT ID
另外还有两个自定义的字段,一个是 name ,还有一个是 admin 。
Signature:
JWT 的最后一部分是 Signature ,这部分内容有三个部分,先是用 Base64 编码的 header.payload ,再用加密算法加密一下,加密的时候要放进去一个 Secret ,这个相当于是一个密码,这个密码秘密地存储在服务端。
最后样子如:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwYXNzd29yZCI6IjEyMyIsImV4cCI6MTY1NTQ1OTI0OSwidXNlcm5hbWUiOiJ6ZGoifQ.zYJWvXg5Oc1-CwRFUXFrfIHtXRAAfOJct39EMh3bAUk
TokenUtil工具类:
public class TokenUtil {
private static String username = "admin" ;
private static String password = "123" ;
//设置30分钟过期
private static final long EXPIRE_DATE=30*60*1000;
//token秘钥
private static final String TOKEN_SECRET = "[自行设置-加密工具串]";
public static String tokenTest (String username,String password){
String token = "";
try {
//过期时间
Date date = new Date(System.currentTimeMillis()+EXPIRE_DATE);
//秘钥及加密算法
Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET);
//第一部分:设置头部信息
Map<String,Object> header = new HashMap<>();
header.put("typ","JWT");
header.put("alg","HS256");
//第二部分:携带username,password信息
token = JWT.create()
.withHeader(header)
.withClaim("username",username)
.withClaim("password",password).withExpiresAt(date)
.sign(algorithm); //第三部分:生成签名
}catch (Exception e){
e.printStackTrace();
return null;
}
return token;
}
//验证token
public static boolean verify(String token){
try {
Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET); //同样的加密算法
JWTVerifier verifier = JWT.require(algorithm).build();
DecodedJWT jwt = verifier.verify(token);
return true;
}catch (Exception e){
e.printStackTrace();
return false;
}
}
基于HttpServer搭建简易服务器:
.setAuthenticator( new BasicAuthenticator() )
实现窗体登陆验证
server.createContext("/", new MyHttpHandler());
处理网页/postman请求
public class httpInter {
static String strClassName = httpInter.class.getName();
static Logger logger = Logger.getLogger(strClassName);
public static void main(String[] args) throws IOException {
HttpServer server = HttpServer.create(new InetSocketAddress(8888), 0);
//HttpHandler RestDemoHandler = new HttpHandler();
HttpContext secureContext = server.createContext("/login", new MyHttpHandler());
String USERNAME="admin";
String PASSWORD="123";
// 网页登陆窗体,访问:http://localhost:8888/login
secureContext.setAuthenticator(new BasicAuthenticator("post") {
@Override
public boolean checkCredentials(String user, String pwd) {
if(user.equals(USERNAME) && pwd.equals(PASSWORD)){
String token = TokenUtil.tokenTest(user,pwd);
System.out.println(token);
System.out.println("login successed.");
return true;
}
else{
System.out.println("login failed.");
return false;
}
}
});
server.createContext("/test", new MyHttpHandler());
//设置服务器的线程池对象
server.setExecutor(Executors.newFixedThreadPool(10));
System.out.println("[*] Waiting for messages.");
server.start();
}
}
这里设置在网页端输入url后:当id和pwd都对了能访问接口,但没有token读不了具体数据。
因为url只能实现get请求,也携带不了对应的headers信息,需要另设插件。
MyHttpHandler响应处理类:
getRequestParam函数:输入HttpExchange,上半部分处理get请求,下半部分处理post请求
private String getRequestParam(HttpExchange httpExchange) throws Exception {
String paramStr = "";
if (httpExchange.getRequestMethod().equals("GET")) {
//GET请求读queryString
Headers h = httpExchange.getRequestHeaders();
if (h.containsKey("Authorization-Token")) {
String checkToken = String.join(".", h.get("Authorization-Token"));
//获取token
if (TokenUtil.verify(checkToken)){
System.out.println("Token Successed");
paramStr = httpExchange.getRequestURI().getQuery();
}else {
paramStr="Token Error";
}
}
else {
paramStr="Token NULL";
}
} else {
//非GET请求读请求体 POST
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(httpExchange.getRequestBody(), "utf-8"));
StringBuilder requestBodyContent = new StringBuilder();
String line = null;
while ((line = bufferedReader.readLine()) != null) {
requestBodyContent.append(line);
}
paramStr = requestBodyContent.toString();
requestBodyContent.append("\r\n");
// 验证 token
String USERNAME="admin";
String PASSWORD="123";
JSONObject jsonObject = JSONObject.parseObject(paramStr);
String user=jsonObject.getString("USERNAME");
String pwd=jsonObject.getString("PASSWORD");
if(user.equals(USERNAME) && pwd.equals(PASSWORD)){
System.out.println("login successed.");
requestBodyContent.append("Token:");
String token = TokenUtil.tokenTest(user,pwd);//使用token工具类生成token串
requestBodyContent.append(token);
}
else{
System.out.println("login failed.");
}
paramStr = requestBodyContent.toString();
}
return paramStr;
}
首先客户端利用postman进行测试,通过post请求发送对应的账户密码,获取token在服务器的响应报文中:
然后发送携带token认证信息在headers的get请求:
(这里获取的参数是null是因为上面的服务器里就没有存放任何数据,有兴趣可以自己连一个数据库 此处略过)
其他处理函数:
public void handle(HttpExchange httpExchange) {
try {
StringBuilder responseText = new StringBuilder();
responseText.append("请求方法:").append(httpExchange.getRequestMethod()).append("<br/>"); //判断get还是post
responseText.append("请求参数:").append(getRequestParam(httpExchange)).append("<br/>");
responseText.append("请求头:<br/>").append(getRequestHeader(httpExchange));
handleResponse(httpExchange, responseText.toString());
} catch (Exception ex) {
ex.printStackTrace();
}
}
/**
* 获取请求头
*/
private String getRequestHeader(HttpExchange httpExchange) {
Headers headers = httpExchange.getRequestHeaders();
return headers.entrySet().stream()
.map((Map.Entry<String, List<String>> entry) -> entry.getKey() + ":" + entry.getValue().toString())
.collect(Collectors.joining("<br/>"));
}
/**
* 处理响应
*/
private void handleResponse(HttpExchange httpExchange, String responsetext) throws Exception {
//生成html
StringBuilder responseContent = new StringBuilder();
responseContent.append("<html>")
.append("<body>")
.append(responsetext)
.append("</body>")
.append("</html>");
String responseContentStr = responseContent.toString();
byte[] responseContentByte = responseContentStr.getBytes("utf-8");
//设置响应头,必须在sendResponseHeaders方法之前设置!
httpExchange.getResponseHeaders().add("Content-Type", "application/json");//text/html;charset=utf-8
//设置响应码和响应体长度,必须在getResponseBody方法之前调用!
httpExchange.sendResponseHeaders(200, responseContentByte.length);
OutputStream out = httpExchange.getResponseBody(); //输出响应报文
out.write(responseContentByte);
out.flush();
out.close();
}