这次面试依然没有通过,面试不通过的原因在于两个技术问题,一个是netty,一个是JWT。都到了我的知识盲区了。一个个来吧。
Netty demo
照着网上的代码,自己写一个demo。首先肯定是添加maven依赖。
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.75.Final</version>
</dependency>
然后再写个服务端处理程序:
package com.youngthing.netty;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import java.nio.charset.StandardCharsets;
/**
* 服务handler
* created at 15/03/2022
*
* @author 花书粉丝
* <a href="mailto://yujianbo@chtwm.com">yujianbo@chtwm.com</a>
* @since 1.0.0
*/
public class MyServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
final ByteBuf buffer = (ByteBuf) msg;
System.out.println(buffer.toString(StandardCharsets.UTF_8));
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
ctx.writeAndFlush(Unpooled.copiedBuffer("Hello, world!".getBytes(StandardCharsets.UTF_8)));
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
ctx.close();
}
}
再写个main函数:
package com.youngthing.netty;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
/**
* 主程序
* created at 15/03/2022
*
* @author 花书粉丝
* <a href="mailto://yujianbo@chtwm.com">yujianbo@chtwm.com</a>
* @since 1.0.0
*/
public class Main {
public static void main(String[] args) {
// 创建两个线程组
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
final ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 128)
.childOption(ChannelOption.SO_KEEPALIVE, true)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new MyServerHandler());
}
});
final ChannelFuture channelFuture = serverBootstrap.bind(8080).sync();
channelFuture.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
但是这写出来的demo,用telnet测试效果并不好,如下图,我敲一个字母就返回一个“Hello, world!”。
netty是不难的,但是确实是自己连demo都没写过,所以面挂了。
JWT
第二个没回答出来的问题是JWT。我根本不知道这是什么东西。那还能怎么办呢?学啊。JWT是Json Web Token的简称。JWT由三部分组成,header、payload、signature。都是json格式再进行base64编码。主要用途就是用于SSO单点登录。客户端将用户名密码提交给服务器用来登录,服务器校验后返回一个JWT。客户端把JWT保存起来,每次请求都发送,服务器则校验JWT,这样就完成了单点登录过程。
第一部分header解密后,一般是这样的:
{"typ":"JWT","alg":"HS256"}
第二部分,尽量小,可以是这样只包含id字段的:
{"uid":1}
第三部分,则是签名,是前两部分先base64编码,再用英文点号拼接,最后再使用算法进行签名。
服务端生成JWT
服务端生成JWT的代码比较简单,无非是熟练使用java的security包。按照前述的规则,直接写代码就是了:
package com.youngthing.jwt;
import javax.crypto.KeyGenerator;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SignatureException;
import java.util.Base64;
/**
* 生成JWT
* created at 15/03/2022
*
* @author 花书粉丝
* <a href="mailto://yujianbo@chtwm.com">yujianbo@chtwm.com</a>
* @since 1.0.0
*/
public class GenerateJwt {
public static void main(String[] args) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {
String header = "{\"typ\":\"JWT\",\"alg\":\"HS256\"}";
String payload = "{\"uid\":1}";
final Base64.Encoder encoder = Base64.getEncoder();
final String encodedHeader = encoder.encodeToString(header.getBytes(StandardCharsets.UTF_8));
final String encodedPayload = encoder.encodeToString(payload.getBytes(StandardCharsets.UTF_8));
final String text = encodedHeader + "." + encodedPayload;
final Mac instance = Mac.getInstance("HMacSHA256");
final SecretKey key = KeyGenerator.getInstance("HMacSHA256").generateKey();
System.out.println(encoder.encodeToString(key.getEncoded()));
instance.init(key);
instance.update(text.getBytes(StandardCharsets.UTF_8));
final byte[] sign = instance.doFinal();
String signature = encoder.encodeToString(sign);
System.out.println(encodedHeader + "." + encodedPayload + "." + signature);
}
}
输出结果为
qT8cGILvTlunyZ7EdR6osd4EYFSgk3B9clgeBWmih6w=
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1aWQiOjF9.QUnuWLLhIwaYRwLXdS7FDC/9zpHcG3fmz1+Nufna4WI=
服务端验证JWT
将产生的JWT发送给客户端,客户端则自己要保存起来。然后请求时放在头里提交给服务器。服务器需要验证,这个时候就用普通的方法校验就行了。
package com.youngthing.jwt;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SignatureException;
import java.util.Base64;
/**
* 校验JWT
* created at 15/03/2022
*
* @author 花书粉丝
* <a href="mailto://yujianbo@chtwm.com">yujianbo@chtwm.com</a>
* @since 1.0.0
*/
public class CheckJwt {
public static void main(String[] args) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {
String key = "qT8cGILvTlunyZ7EdR6osd4EYFSgk3B9clgeBWmih6w=";
String token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1aWQiOjF9.QUnuWLLhIwaYRwLXdS7FDC/9zpHcG3fmz1+Nufna4WI=";
String text = token.substring(0, token.lastIndexOf('.'));
final Base64.Decoder decoder = Base64.getDecoder();
final Base64.Encoder encoder = Base64.getEncoder();
final Mac instance = Mac.getInstance("HMacSHA256");
instance.init(new SecretKeySpec(decoder.decode(key), "HMacSHA256"));
instance.update(text.getBytes(StandardCharsets.UTF_8));
final byte[] sign = instance.doFinal();
String signature = encoder.encodeToString(sign);
System.out.println(signature);
final String clientSignature = token.substring(token.lastIndexOf('.') + 1);
System.out.println(clientSignature);
System.out.println(clientSignature.equals(signature));
}
}
自此,面试经历整理完毕。