利用Maven搭建的SSH实现带有验证码和密码加密方式的登陆功能。
搭建SSH
首先搭建SSH,可以参考利用Maven搭建SSM项目
配置实体类
利用注解配置User实体类,然后利用Hibernate生成相应的数据表。注意要在Hibernate配置文件中配置实体类的位置和表的生成策略。
@Entity
@Table(name = "user")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long user_id;
@Column
private String user_code;
@Column
private String user_name;
@Column
private String user_password;
@Column
private Character user_state;
public String getUser_code() {
return user_code;
}
public void setUser_code(String user_code) {
this.user_code = user_code;
}
public String getUser_name() {
return user_name;
}
public void setUser_name(String user_name) {
this.user_name = user_name;
}
public String getUser_password() {
return user_password;
}
public Long getUser_id() {
return user_id;
}
public void setUser_id(Long user_id) {
this.user_id = user_id;
}
public void setUser_password(String user_password) {
this.user_password = user_password;
}
public Character getUser_state() {
return user_state;
}
public void setUser_state(Character user_state) {
this.user_state = user_state;
}
}
前端展示页面
一个带有基本功能的展示页面,用Ajax的post方式提交表单数据,如果状态码等于200的话登陆成功,否则登陆失败,打印出登陆失败的信息并刷新验证码。
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>登陆界面</title>
<script src="http://libs.baidu.com/jquery/2.0.0/jquery.js"></script>
</head>
<body>
<h3>用户登陆</h3>
<form action="LoginAction_login.do" id="form1" method="post">
<table>
<tr>
<td><label for="username">用户名:</label></td>
<td><input type="text" name="userName" id="username" /></td>
</tr>
<tr>
<td><label for="password">密码:</label></td>
<td><input type="password" name="password" id="password" /></td>
</tr>
<tr>
<td><label for="verify">验证码:</label></td>
<td><input type="text" name="verify" id="verify" /></td>
</tr>
<tr>
<td></td>
<td><img src="getVerifyAction.do" id="imgCode" /></td>
</tr>
<tr>
<td></td>
<td><input type="submit" /></td>
</tr>
</form>
</table>
</body>
<script type="text/javascript">
$(function() {
$("#imgCode").click(function() {
refreshCode();
});
$("#form1").submit(
function() {
$.post("LoginAction_login.do", $("#form1").serialize(),
function(data) {
if (data.state == 200) {
//跳转界面
alert("success");
} else {
alert(data.msg);
refreshCode();
}
})
return false;
})
})
//刷新验证码
function refreshCode() {
var url = "getVerifyAction.do?" + new Date().getTime(); //加时间戳防止缓存
$("#imgCode").attr("src", url);
}
</script>
</html>
后端逻辑
首先是dao层,同过service层传入的用户名判断是否存在该用户,没有则返回null
public User findUserByName(String name) {
String sql = "from User where user_name = ?";
List<User> lists = (List<User>) getHibernateTemplate().find(sql, name);
if (lists.isEmpty()) {
return null;
}
return lists.get(0);
}
然后是service层,通过controller层传入的User 用户,然后通过判断是否存在该用户,没有则直接抛出异常。有则继续判断密码加密后是否相同。
public void login(User u) {
// TODO Auto-generated method stub
User u1 = dao.findUserByName(u.getUser_name());
if (u1 == null) {
throw new RuntimeException("用户民不存在");
}
if (!u1.getUser_password().equals(MD5Utils.md5(u.getUser_password()))) {
throw new RuntimeException("密码错误");
}
}
生成随机的数字,然后把随机数让入session中,输出到页面。注意验证码是一个独立的Action,直接在img标签的src属性中写入验证码的Action,就会生成验证码图片。或者直接在浏览器输入验证码的Action,就会出现验证码的图片。
@Controller("getVerifyAction")
@Scope("prototype")
public class GetVerifyAction {
/**
* 设置验证码
*
* @return
* @throws IOException
* @date 2017年10月15日 上午9:41:38
*/
public String getVerifyImpage() throws IOException {
HttpServletResponse response = ServletActionContext.getResponse();
HttpServletRequest request = ServletActionContext.getRequest();
response.setContentType("image/jpeg");
response.setHeader("Pragma", "No-cache");
response.setHeader("Cache-Control", "no-cache");
response.setDateHeader("Expires", 0);
HttpSession session = request.getSession();
session.removeAttribute("verifyCode");
int width = 70, height = 23;
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
// 获取图形上下文
Graphics g = image.getGraphics();
// 生成随机类
Random random = new Random();
// 设定背景色
g.setColor(getRandColor(200, 250));
g.fillRect(0, 0, width, height);
// 设定字体
g.setFont(new Font("Times New Roman", Font.PLAIN, 20));
// 画边框
g.setColor(getRandColor(160, 200));
g.drawRect(0, 0, width - 1, height - 1);
// 随机产生100条干扰线
g.setColor(getRandColor(160, 200));
for (int i = 0; i < 100; i++) {
int x = random.nextInt(width);
int y = random.nextInt(height);
int xl = random.nextInt(10);
int yl = random.nextInt(10);
g.drawLine(x, y, x + xl, y + yl);
}
// 取随机产生4位数字
String sRand = "";
for (int i = 0; i < 4; i++) {
String rand = String.valueOf(random.nextInt(10));
sRand += rand;
g.setColor(new Color(20 + random.nextInt(110), 20 + random.nextInt(110), 20 + random.nextInt(110)));
g.drawString(rand, 13 * i + 14, 20);
}
// 将认证码存入SESSION
session.setAttribute("verifyCode", sRand);
System.out.println("verifyCode:" + session.getAttribute("verifyCode"));
// 图象生效
g.dispose();
// 输出图象到页面
ImageIO.write(image, "JPEG", response.getOutputStream());
return null;
}
private Color getRandColor(int fc, int bc) {// 给定范围获得随机颜色
Random random = new Random();
if (fc > 255)
fc = 255;
if (bc > 255)
bc = 255;
int r = fc + random.nextInt(bc - fc);
int g = fc + random.nextInt(bc - fc);
int b = fc + random.nextInt(bc - fc);
return new Color(r, g, b);
}
}
登陆的Action,首先判断前端传入的验证码是否和session中的验证相等。
相等则把前台传入的账号,密码封装到User实体中,传入到service中。当然也可以不封装,直接把用户名,密码传入service,前台参数少可以这么做。如果前台传入的参数很多的话,最好封装到相对应的实体类中,一方面是更能体现出相对对象的写法,另一方面是写出的代码更加利于维护和简洁。
调用完service层方法后,如果没有抛出异常,则说明用户名和密码匹配。如果不匹配,就会抛出相应的异常,并且捕捉到Map集合中。
public class LoginAction extends ActionSupport {
@Autowired
LoginService service;
/**
* 登陆
*
* @return
* @throws IOException
* @date 2017年10月15日 下午5:42:47
*/
public String login() throws IOException {
Map m = new HashMap();
boolean falg = ResultUtils.checkVerifyCode(verify);
if (falg) {
User u = new User();
u.setUser_name(userName);
u.setUser_password(password);
try {
service.login(u);
m.put("state", Result.SUCCESS.getState());
} catch (Exception e) {
// TODO: handle exception
String message = e.getMessage();
m.put("state", Result.ERROR.getState());
m.put("msg", message);
}
} else {
m.put("state", Result.ERROR.getState());
m.put("msg", "验证码错误");
}
//把Map集合转化为Json数据
ResultUtils.toJson(ServletActionContext.getResponse(), m);
return null;
}
private String userName;
private String password;
private Map<String, Object> jsonResult;
private String verify;
public String getVerify() {
return verify;
}
public void setVerify(String verify) {
this.verify = verify;
}
public Map<String, Object> getJsonResult() {
return jsonResult;
}
public void setJsonResult(Map<String, Object> jsonResult) {
this.jsonResult = jsonResult;
}
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;
}
}
枚举类型的结果集,创建通过整形初始化和字符串和整形初始化的构造器。比如当调用Result.SUCCESS(200),可以初始化一个state为200的对象。然后Result.SUCCESS(200).getState()就可以获取该参数。
public enum Result {
SUCCESS(200), ERROR(400), INFO("请求超时", 100);
private String msg;
private int state;
private Result(int state) {
this.state = state;
}
private Result(String msg, int state) {
this.msg = msg;
this.state = state;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
}
}
MD5加密的工具类,传入相应的密码就会获取该密码的加密的字符串。
public class MD5Utils {
/**
* 使用md5的算法进行加密
*/
public static String md5(String plainText) {
byte[] secretBytes = null;
try {
secretBytes = MessageDigest.getInstance("md5").digest(plainText.getBytes());
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("没有md5这个算法!");
}
String md5code = new BigInteger(1, secretBytes).toString(16);// 16进制数字
// 如果生成数字未满32位,需要前面补0
for (int i = 0; i < 32 - md5code.length(); i++) {
md5code = "0" + md5code;
}
return md5code;
}
public static void main(String[] args) {
System.out.println(md5("1234"));
}
}
由于把对象转化为Json数据和检查验证码是否相等,在许多地方都能够用到,因此把大量重复的代码提取出来,制作成工具类。更加使代码简洁,优雅,也更能体现面对对象的思想。
public class ResultUtils {
/**
* 转化json数据
*
* @date 2017年10月15日 下午4:03:29
*/
public static void toJson(HttpServletResponse response, Object data) throws IOException {
Gson gson = new Gson();
String result = gson.toJson(data);
response.setContentType("text/json; charset=utf-8");
response.setHeader("Cache-Control", "no-cache"); // 取消浏览器缓存
PrintWriter out = response.getWriter();
out.print(result);
out.flush();
out.close();
}
/**
* 验证验证码是否相等
*
* @param originVerifyCode
* @return
* @date 2017年10月15日 上午9:47:30
*/
public static boolean checkVerifyCode(String originVerifyCode) {
HttpServletRequest request = ServletActionContext.getRequest();
HttpSession session = request.getSession();
String verifyCode = (String) session.getAttribute("verifyCode");
if (originVerifyCode.equals(verifyCode)) {
return true;
}
return false;
}
}
展示效果
成功
密码错误
用户名不存在