无服务器探索之路(初级):AWS Lambda服务应用场景实践之一(八)

时间:2022-03-14 19:58:02

身份验证模块

项目目录结构

无服务器探索之路(初级):AWS Lambda服务应用场景实践之一(八)
1、新建maven工程

Group Id:org.apache.maven.archetypes
Artifact Id:maven-archetype-quickstart
Version:1.1

2、引入依赖
pom.xml

<groupId>pers.zhentao</groupId>
    <artifactId>Validator</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>Validator</name>
    <url>http://maven.apache.org</url>
    <packaging>jar</packaging>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.6.0</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                    <forceJavacCompilerUse>true</forceJavacCompilerUse>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>2.3</version>
                <configuration>
                    <createDependencyReducedPom>false</createDependencyReducedPom>
                </configuration>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.amazonaws</groupId>
            <artifactId>aws-lambda-java-core</artifactId>
            <version>1.1.0</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.38</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>6.0.6</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt -->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.7.0</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.amazonaws/aws-java-sdk-lambda -->
        <dependency>
            <groupId>com.amazonaws</groupId>
            <artifactId>aws-java-sdk-lambda</artifactId>
            <version>1.11.221</version>
        </dependency>
    </dependencies>

3、lambda函数输入POJO
RequestInfo.java

/** * 请求封装 * * @author zhangzhentao1995@163.com * 2017-10-30 */
public class RequestInfo {
    public static final int TARGET_ADD_RECORD = 3001;
    public static final int TARGET_DELETE_RECORD = 3002;
    public static final int TARGET_QUERY_RECORD = 3003;
    /** * token */
    private String token;
    /** * 目标接口 */
    private int target;
    /** * 参数Json */
    private String paramJson;

    //getters and setters...
}

4、lambda函数输出POJO
ResponseData.java

/** * 返回消息 * * @author zhangzhentao1995@163.com * 2017-10-30 */
public class ResponseData {
    public static final Integer RESPONSE_CODE_SUCCESS = 1000;
    public static final Integer RESPONSE_CODE_ERROR = 1001;
    public static final Integer RESPONSE_CODE_SYS_EXCEPTION = 1002;

    private Integer code;
    private String message;
    private List<Record> rows;
    private Map<Object, Object> map;

    public ResponseData(Integer code, String message) {
        this.code = code;
        this.message = message;
    }

    public ResponseData(Integer code, List<Record> rows) {
        this.rows = rows;
        this.code = code;
    }

    //getters and setters...
}

5、lambda处理程序
LambdaFunctionHandler.java

/** * lambda处理程序 * * @author zhangzhentao1995@163.com * 2017-10-30 */
public class LambdaFunctionHandler implements RequestHandler<RequestInfo, ResponseData> {
    private static String awsAccessKeyId = null;
    private static String awsSecretAccessKey = null;
    private static String regionName = null;
    private static String jwtSecret = null;

    public ResponseData handleRequest(RequestInfo input, Context context) {
        LambdaLogger logger = context.getLogger();
        awsAccessKeyId = System.getenv("ACCESS_KEY");
        awsSecretAccessKey = System.getenv("SECRET_KEY");
        regionName = System.getenv("REGION");
        jwtSecret = System.getenv("JWT_SECRET");
        if(awsAccessKeyId == null || awsSecretAccessKey == null || regionName == null) {
            return new ResponseData(ResponseData.RESPONSE_CODE_ERROR, "aws config is null.");
        }
        if(jwtSecret == null) {
            return new ResponseData(ResponseData.RESPONSE_CODE_ERROR, "jwtSecret is null.");
        }
        logger.log("[" + new Date() + "]RequestInfo:" + JSONObject.toJSONString(input));
        User user = TokenUtil.validateToken(input.getToken(), jwtSecret);
        logger.log("[" + new Date() + "]user:" + JSONObject.toJSONString(user));
        if (user == null) {
            return new ResponseData(ResponseData.RESPONSE_CODE_ERROR, "invalid token.");
        } else {
            AWSCredentials credentials = new BasicAWSCredentials(awsAccessKeyId, awsSecretAccessKey);
            AWSStaticCredentialsProvider provider = new AWSStaticCredentialsProvider(credentials);
            LambdaFunctions functions = LambdaInvokerFactory.builder().lambdaClient(
                    AWSLambdaAsyncClientBuilder.standard().withCredentials(provider).withRegion(regionName).build())
                    .build(LambdaFunctions.class);
            switch (input.getTarget()) {
            case RequestInfo.TARGET_ADD_RECORD:
                try {
                    Record record = JSONObject.parseObject(input.getParamJson(), Record.class);
                    record.setCreatedBy(user.getUserId());
                    Object addRecordResponse = functions.AddRecord(record);
                    logger.log("[" + new Date() + "]invoke addRecordFunction:"
                            + JSONObject.toJSONString(addRecordResponse));
                    return new ResponseData(
                            JSONObject.parseObject(JSONObject.toJSONString(addRecordResponse)).getInteger("code"),
                            JSONObject.parseObject(JSONObject.toJSONString(addRecordResponse)).getString("message"));
                } catch (Exception e) {
                    logger.log("[" + new Date() + "]exception:" + e.getMessage());
                    return new ResponseData(ResponseData.RESPONSE_CODE_ERROR, "invalid input.");
                }
            case RequestInfo.TARGET_DELETE_RECORD:
                Object deleteRecordResponse = functions.DeleteRecord(Integer.parseInt(input.getParamJson()));
                logger.log("[" + new Date() + "]invoke deleteRecordFunction:"
                        + JSONObject.toJSONString(deleteRecordResponse));
                return new ResponseData(
                        JSONObject.parseObject(JSONObject.toJSONString(deleteRecordResponse)).getInteger("code"),
                        JSONObject.parseObject(JSONObject.toJSONString(deleteRecordResponse)).getString("message"));
            case RequestInfo.TARGET_QUERY_RECORD:
                try {
                    Record record2 = JSONObject.parseObject(input.getParamJson(), Record.class);
                    Object queryRecordResponse = functions.QueryRecord(record2);
                    logger.log("[" + new Date() + "]invoke queryRecordFunction:"
                            + JSONObject.toJSONString(queryRecordResponse));
                    Integer code = JSONObject.parseObject(JSONObject.toJSONString(queryRecordResponse))
                            .getInteger("code");
                    if (code.equals(ResponseData.RESPONSE_CODE_SUCCESS)) {
                        return new ResponseData(code,
                                JSONObject.parseObject(JSONObject.toJSONString(queryRecordResponse)).getObject("rows",
                                        List.class));
                    }
                } catch (Exception e) {
                    logger.log("[" + new Date() + "]exception:" + e.getMessage());
                    return new ResponseData(ResponseData.RESPONSE_CODE_ERROR, "invalid input.");
                }
            default:
                return new ResponseData(ResponseData.RESPONSE_CODE_ERROR, "invalid target.");
            }
        }
    }

}

6、lambda调用接口
LambdaFunctions.java

/** * lambda接口 * * @author zhangzhentao1995@163.com * 2017-10-30 */
public interface LambdaFunctions {
    /** * 调用添加接口 * * @param record * @return */
    @LambdaFunction
    Object AddRecord(Record record);

    /** * 调用删除接口 * * @param id * @return */
    @LambdaFunction
    Object DeleteRecord(Integer id);

    /** * 调用查询接口 * * @param record * @return */
    @LambdaFunction
    Object QueryRecord(Record record);
}

7、通讯录实体类
Record.java

/** * 通讯录实体类 * * @author zhangzhentao1995@163.com * 2017-10-30 */
public class Record {
    private Integer addressBookId;
    private String name;
    private String phoneNumber;
    private String address;
    private String telephoneNumber;
    private String note;
    private Date createTime;
    private Integer createdBy;

    //getters and setters...
}

8、user实体类
User.java

/** * user dto * * @author zhangzhentao1995@163.com * 2017-10-30 */
public class User {
    private Integer userId;
    private String userName;
    private String email;
    private Date createDate;

    //getters and setters...
}

9、JWT配置常量
Constant.java

/** * JWT配置常量 * * @author zhangzhentao1995@163.com * 2017-10-30 */
public class Constant {
    public static final String JWT_ID = "jwt";
    public static final int JWT_TTL = 60*60*1000;
    public static final int JWT_REFRESH_INTERVAL = 55*60*1000;
    public static final int JWT_REFRESH_TTL = 12*60*60*1000;
}

10、JWT封装工具类
JwtUtil.java

/** * JWT封装工具类 * * @author zhangzhentao1995@163.com * 2017-10-30 */
public class JwtUtil {

    public static SecretKey generalKey(String jwtSecret) {
        byte[] encodeKey = Base64.getDecoder().decode(jwtSecret);
        SecretKey key = new SecretKeySpec(encodeKey, 0, encodeKey.length, "AES");
        return key;
    }

    public static String createJwt(String id, String jwtSecret, String subject, long ttlMillis) throws Exception {
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
        long nowMillis = System.currentTimeMillis();
        Date now = new Date(nowMillis);
        SecretKey key = generalKey(jwtSecret);
        JwtBuilder builder = Jwts.builder().setId(id).setIssuedAt(now).setSubject(subject).signWith(signatureAlgorithm,
                key);
        if (ttlMillis >= 0) {
            long expMillis = nowMillis + ttlMillis;
            Date exp = new Date(expMillis);
            builder.setExpiration(exp);
        }
        return builder.compact();
    }

    public static Claims parseJwt(String jwt, String jwtSecret) throws Exception {
        SecretKey key = generalKey(jwtSecret);
        Claims claims = Jwts.parser().setSigningKey(key).parseClaimsJws(jwt).getBody();
        return claims;
    }

    public static String generalSubject(User user) {
        JSONObject json = new JSONObject();
        json.put("userId", user.getUserId());
        json.put("userName", user.getUserName());
        json.put("email", user.getEmail());
        json.put("createDate", user.getCreateDate());
        return json.toString();
    }
}

11、token工具类
TokenUtil.java

/** * token工具类 * * @author zhangzhentao1995@163.com * 2017-10-30 */
public class TokenUtil {

    public static User validateToken(String token, String jwtSecret) {
        try {
            Claims claims = JwtUtil.parseJwt(token, jwtSecret);
            String subject = claims.getSubject();
            User user = JSONObject.toJavaObject(JSONObject.parseObject(subject), User.class);
            return user;
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return null;
    }

    public static String getToken(User user, String jwtSecret) {
        String subject = JwtUtil.generalSubject(user);
        String token = null;
        try {
            token = JwtUtil.createJwt(Constant.JWT_ID, jwtSecret, subject, Constant.JWT_TTL);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return token;
    }

}

12、maven打包

mvn package

执行完成后,项目target文件夹下会出现jar包:XXXX-0.0.1-SNAPSHOT.jar
13、创建lambda函数,配置处理程序路径,配置环境变量,上传jar包
无服务器探索之路(初级):AWS Lambda服务应用场景实践之一(八)
14、配置测试事件

测试Add:

无服务器探索之路(初级):AWS Lambda服务应用场景实践之一(八)

测试Delete

无服务器探索之路(初级):AWS Lambda服务应用场景实践之一(八)

测试Query

无服务器探索之路(初级):AWS Lambda服务应用场景实践之一(八)


至此,身份验证模块lambda部分完成。
接下来进行API Gateway配置。
15、创建API
无服务器探索之路(初级):AWS Lambda服务应用场景实践之一(八)
16、创建资源
无服务器探索之路(初级):AWS Lambda服务应用场景实践之一(八)
无服务器探索之路(初级):AWS Lambda服务应用场景实践之一(八)
(启用 API Gateway CORS,解决Ajax跨域访问问题)
17、创建方法,选择集成环境,选择区域和Lambda函数,保存
无服务器探索之路(初级):AWS Lambda服务应用场景实践之一(八)
无服务器探索之路(初级):AWS Lambda服务应用场景实践之一(八)
18、配置POST方法,配置方法响应,添加200的响应标头(解决Ajax跨域访问问题)

Access-Control-Allow-Origin

无服务器探索之路(初级):AWS Lambda服务应用场景实践之一(八)
19、配置集成响应,设置响应标头映射值

标头:Access-Control-Allow-Origin
值(注意加入单引号):'*'

无服务器探索之路(初级):AWS Lambda服务应用场景实践之一(八)
20、部署API
无服务器探索之路(初级):AWS Lambda服务应用场景实践之一(八)
21、测试API
测试报文:

{
    "username":"test",
    "password":"test" }

无服务器探索之路(初级):AWS Lambda服务应用场景实践之一(八)
查看测试结果是否正确。
22、使用相同方式配置资源:operation和register
无服务器探索之路(初级):AWS Lambda服务应用场景实践之一(八)


至此,身份验证模块完成。