1.待测试的接口说明
1、自己写的一个登录功能,通过加密算法获得私钥,token还有cookie,通过Postman访问该接口
- 接口的地址:http://localhost:8080/login
- 请求的方式:post
- 请求的结果:
2、测试目标
登录成功后,返回是否返回预期的状态和token
2.新建Java web项目
1、工程结构说明
2、User.java源码
package com.test.entities;
/**
* 用户实例,每个字段对应数据库user表的每个字段
*/
public class User {
private String username;
private String password;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User [username=" + username + ", password=" + password + "]";
}
}
3、JdbcUtils.java源码
package com.test.utils;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
/**
* 数据库连接常用类
*
*/
public class JdbcUtils {
/**
* 进行数据库连接,并返回连接对象
* @return 数据库连接对象
* @throws Exception
*/
public static Connection getConnection() throws Exception {
String url = "jdbc:mysql:///数据库名?characterEncoding=utf8&useSSL=false";
String username = "用户名";
String password = "密码";
String driver = "com.mysql.jdbc.Driver";
Class.forName(driver); // 将数据库连接驱动driver加载到内存中
return DriverManager.getConnection(url, username, password); // 返回连接对象
}
/**
* 关闭数据库连接的静态方法
* @param conn 需要关闭的连接对象
* @param stmt 需要关闭的语句对象
* @param rs 需要关闭的查询结果对象
*/
public static void close(Connection conn, Statement stmt, ResultSet rs) {
if(rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(conn!=null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
4、UserDao.java源码
package com.test.dao;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;
import com.test.entities.User;
import com.test.utils.JdbcUtils;
public class UserDao {
/**
* 根据给定的sql和参数进行数据库查询,并将结果转成对象返回
* @param sql 需要执行的select语句
* @param args select查询语句中占位符所需的参数
* @return select查询后返回的对象集合
*/
public List<User> getUsers(String sql, Object...args){
List<User> users = new ArrayList<>();
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = JdbcUtils.getConnection(); // 获取数据库连接
ps = conn.prepareStatement(sql); // 创建带有占位符的语句对象
for(int i = 0; i < args.length; i++) {
ps.setObject(i+1, args[i]); // 对占位符进行实际赋值
}
rs = ps.executeQuery(); // 执行sql语句,并获取返回
while(rs.next()) { // 根据返回进行记录的逐条遍历
User _user = new User();
_user.setUsername(rs.getString("username"));
_user.setPassword(rs.getString("password"));
users.add(_user); // 每一条记录代表一个user对象,添加到集合中
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JdbcUtils.close(conn, ps, rs); // 关闭数据库连接
}
return users;
}
}
5、Authorization .java源码
package com.test.entities;
/**
* 返回给成功登录的授权用户当前的token
*
*/
public class Authorization {
/**
* 状态信息
*/
private String status;
/**
* token信息
*/
private String token;
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
@Override
public String toString() {
return "Authorization [status=" + status + ", token=" + token + "]";
}
}
6、SearchError .java源码
package com.test.entities;
/**
* 用于返回用户各类访问错误的实例
*
*/
public class SearchError {
/**
* 错误编号
*/
private int errorCode;
/**
* 错误信息
*/
private String message;
public int getErrorCode() {
return errorCode;
}
public void setErrorCode(int errorCode) {
this.errorCode = errorCode;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
@Override
public String toString() {
return "SearchError [errorCode=" + errorCode + ", message=" + message + "]";
}
}
7、UserService .java源码
package com.test.services;
import java.util.ArrayList;
import java.util.List;
import com.test.dao.UserDao;
import com.test.entities.User;
public class UserService {
/**
* 用于对输入指定筛选条件信息数据库查询的方法
* @param userName 用户名
* @param password 密码
* @return 所有符合条件的user对象集合
*/
public List<User> getUsers(String userName, String password){
List<User> users = null;
String sql = "select * from tb_User where "; // 不完整的sql语句,最后的where用于和之后的筛选条件拼接
List<Object> listArgs = new ArrayList<>(); // 用于输入到sql中的占位参数,对应sql中“?”占位符
if(userName!=null && !userName.equals("")) { // 如果username不为空
sql = sql + "username=? and "; // sql语句中拼接username的筛选条件
listArgs.add(userName); // 将username的值加入到参数中
}
if(password!=null && !password.equals("")) { // 如果password不为空
sql = sql + "password =? and"; // 拼接password的筛选条件
listArgs.add(password); // 将查询条件加入参数集合中
}
// 最后拼接1=1恒true表达式,用于结尾,否则sql最后留下一个and关键字,语法错误
sql=sql+"and 1=1";
UserDao userDao = new UserDao();
users = userDao.getUsers(sql, listArgs.toArray()); // 将组织好的sql,和传递的参数交给DAO进行数据库实际访问
return users;
}
}
8、BearerToken .java源码
package com.test.utils;
import java.util.Date;
import javax.crypto.SecretKey;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
/**
* 用于生成token
*
*/
public class BearerToken {
/**
* 生成token的静态方法
* @param key 加密私钥
* @param username 授权用户的用户名
* @param expir token过期时间
* @return token字符串
*/
public static String createToken(SecretKey key, String username, Date expir) {
SignatureAlgorithm sa = SignatureAlgorithm.HS256; // 设置签名算法
JwtBuilder builder = Jwts.builder()
.setAudience(username) // 授权用户
.setExpiration(expir) // 过期时间
.signWith(sa, key); // 签名算法和私钥
return builder.compact();
}
}
9、CreateKey .java源码
package com.test.utils;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
/**
* 用于生成私钥
*
*/
public class CreateKey {
/**
* 生成私钥的静态方法
* @return
*/
public static SecretKey genKey() {
String seed = System.currentTimeMillis()+""; // 私钥的生成是根据当前系统时间的毫秒表达式
return new SecretKeySpec(seed.getBytes(), "AES"); //通过AES算法将字节数组加密为私钥
}
}
10、Login.java源码-实现用户登录授权的接口
package com.test.jk;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;
import java.util.List;
import javax.crypto.SecretKey;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.test.entities.Authorization;
import com.test.entities.SearchError;
import com.test.entities.User;
import com.test.services.UserService;
import com.test.utils.BearerToken;
import com.test.utils.CreateKey;
/**
* Servlet implementation class Login
* 实现用户登录授权的接口,要求用户通过post请求传递user和pwd两个参数,分别代表用户的注册用户名和密码。
* 如果用户名和密码校验成功,则会生成一小时有效的token,并将token返回给用户,同时在用户端生成cookie,用于记录过期时间。
*/
public class Login extends HttpServlet {
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String username = request.getParameter("user");
String password = request.getParameter("pwd");
Gson gson = new GsonBuilder().create();
String json = null;
if (username != null && password != null) { // 如果用户名和密码不为空
List<User> user=new UserService().getUsers(username, password);//使用数据库数据来校验用户名和密码
if (user.size()>0) {
SecretKey key = null;
Object obj = getServletContext().getAttribute("secretKey");
if(obj==null){//如果没有secretKey属性,则说明之前未生成过服务器端加密用的私钥
key=CreateKey.genKey();//获得一个新的私钥
getServletContext().setAttribute("secretKey", key);
}else{
key=(SecretKey)obj;
}
Date expir=new Date(System.currentTimeMillis()+3600000);//设置token的过期时间为一个小时
Authorization auth=new Authorization();
auth.setStatus("authoried");//将返回对象的status属性设为已授权
auth.setToken(BearerToken.createToken(key, username, expir));//调用createToken方法对用户名和过期时间进行签名生成token
json=gson.toJson(auth);//将auth对象转成json格式
Cookie cookie=new Cookie("expir", expir.getTime()+"");//设置客户端需要保存的cookie,名字是expir,值时过期时间的毫秒表达式
cookie.setMaxAge(3600);//设置cookie过期的时间是1小时
response.addCookie(cookie);//将cookie发送到客户端
} else {//如果用户名和密码不正确,设置错误返回对象
SearchError error = new SearchError();
error.setErrorCode(101);
error.setMessage("错误的用户名或密码");
json = gson.toJson(error);
}
} else {//当未提交用户名或者密码,则设置错误返回对象
SearchError error = new SearchError();
error.setErrorCode(102);
error.setMessage("未提供用户名和密码进行身份验证");
json = gson.toJson(error);
}
response.setCharacterEncoding("utf-8");
response.setContentType("application/json");
PrintWriter out = response.getWriter();
out.print(json);
out.flush();
}
}
4、使用PostMan访问地址接口
1、启动eclipse的tomcat服务
2、在PostMan新建一个Request,输入地址,选择请求方法
3、在body里面输入用户名和密码(数据库已注册的用户名密码)
4、点击send按钮