使用token、MD5和JWT实现登录注册的过程可以提高系统的安全性和用户体验。下面是一个简单的流程:
注册:
- 用户提供用户名和密码。
- 服务器接收到用户名和密码后,使用MD5对密码进行加密,然后将用户名和加密后的密码保存到数据库中。
登录:
- 用户提供用户名和密码。
- 服务器接收到用户名和密码后,使用MD5对密码进行加密,然后与数据库中保存的加密密码进行比对。
- 如果密码匹配成功,服务器生成一个JWT(JSON Web Token)并返回给客户端。
JWT(JSON Web Token):
- JWT是一种用于认证和授权的安全传输数据的方式。
- JWT由三部分组成:头部、载荷和签名。
- 头部包含了算法和令牌类型的信息。
- 载荷包含了用户的身份信息,比如用户名、用户ID等。
- 签名用于验证令牌的真实性和完整性。
请求验证:
- 客户端在每次请求中将JWT放在请求头中发送给服务器。
- 服务器接收到请求后,解析JWT,并验证签名的有效性。
- 如果验证通过,服务器可以使用载荷中的用户信息进行后续的操作。
为什么要使用Token、MD5以及JWT?
Token:使用Token可以避免在每次请求中都需要发送用户名和密码,减少了安全性风险。
MD5:使用MD5对密码进行加密可以增加密码的安全性,即使数据库泄露也不会直接暴露用户的明文密码。
JWT:使用JWT可以在服务器和客户端之间传输安全的认证和授权信息,减少了对服务器的请求次数,提高了系统的性能和扩展性。同时,JWT是无状态的,服务器不需要保存会话信息,使得系统更易于水平扩展。
代码实现:
model层:
/**
* 登录 request
*/
public class LoginRequest {
private String userPhone;
private String password;
public String getUserPhone() {
return userPhone;
}
public void setUserPhone(String userPhone) {
this.userPhone = userPhone;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
mapper层:
public interface UserMapper {
int save(User user);
User findByPhoneAndPassword(@Param("userPhone") String userPhone, @Param("password") String password);
}
mapper.xml:
```java
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="net.System.mapper.UserMapper">
<insert id="save" parameterType="User">
insert into sys_user(user_phone, password, user_petname)
values (#{userPhone}, #{password}, #{userPetName})
</insert>
<!-- 根据手机号和密码查询用户信息 -->
<select id="findByPhoneAndPassword" resultType="User">
select * from sys_user where user_phone = #{userPhone} and password = #{password}
</select>
</mapper>
service层:
public interface UserService {
int save (Map<String, String> userInfo);
String findByPhoneAndPassword(String userPhone, String password);
}
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
public int save(Map<String, String> userInfo) {
User user = parseToUser(userInfo);
if( user != null){
return userMapper.save(user);
}else {
return -1;
}
}
@Override
public String findByPhoneAndPassword(String userPhone, String password) {
User user = userMapper.findByPhoneAndPassword(userPhone,CommonUtils.MD5(password));
if(user == null){
return null;
}
else{
String token = JWTUtils.geneJsonWebToken(user);
return token;
}
}
/**
* 解析 user 对象
* @param userInfo
* @return
*/
private User parseToUser(Map<String,String> userInfo) {
if(userInfo.containsKey("userPhone") && userInfo.containsKey("password")){
User user = new User();
user.setUserPetName("NW"+userInfo.get("userPhone"));
user.setUserPhone(userInfo.get("userPhone"));
String password = userInfo.get("password");
//MD5加密
user.setPassword(CommonUtils.MD5(password));
return user;
}else {
return null;
}
}
}
controller层:
@RestController
@RequestMapping("api/v1/pri")
public class UserController {
@Autowired
private UserService userService;
@CrossOrigin
@PostMapping("register")
public JsonData register(@RequestBody Map<String, String> userInfo){
int rows = userService.save(userInfo);
return rows == 1 ? JsonData.buildSuccess(): JsonData.buildError("注册失败,改账号以存在");
}
@CrossOrigin
@PostMapping("login")
public JsonData login(@RequestBody LoginRequest loginRequest){
String token = userService.findByPhoneAndPassword(loginRequest.getUserPhone(), loginRequest.getPassword());
return token == null ?JsonData.buildError("登录失败,账号密码错误"): JsonData.buildSuccess(token);
}
}
登录拦截器:
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
try {
String accesToken = request.getHeader("token");
if (accesToken == null) {
accesToken = request.getParameter("token");
}
/**
* 解密
*/
if (StringUtils.isNotBlank(accesToken)) {
Claims claims = JWTUtils.checkJWT(accesToken);
if (claims == null) {
//告诉登录过期,重新登录
sendJsonMessage(response, JsonData.buildError("登录过期,重新登录"));
return false;
}
String userPhone = (String) claims.get("userPhone");
request.setAttribute("userPhone", userPhone);
return true;
}
}catch (Exception e){}
sendJsonMessage(response, JsonData.buildError("登录过期,重新登录"));
return false;
}
/**
* 响应json数据给前端
* @param response
* @param obj
*/
public static void sendJsonMessage(HttpServletResponse response, Object obj){
try{
ObjectMapper objectMapper = new ObjectMapper();
response.setContentType("application/json; charset=utf-8");
PrintWriter writer = response.getWriter();
writer.print(objectMapper.writeValueAsString(obj));
writer.close();
response.flushBuffer();
}catch (Exception e){
e.printStackTrace();
}
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
@Bean
LoginInterceptor loginInterceptor(){
return new LoginInterceptor();
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(corsInterceptor()).addPathPatterns("/**");
//拦截全部
registry.addInterceptor(loginInterceptor()).addPathPatterns("/api/v1/pri/*/*/**")
//不拦截路径
.excludePathPatterns("/api/v1/pri/login","/api/v1/pri/register");
WebMvcConfigurer.super.addInterceptors(registry);
}
}
utils工具类:
public class CommonUtils {
/**
* MD5加密工具类(加密密码)
* @param data
* @return
*/
public static String MD5(String data) {
try {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] array = md.digest(data.getBytes("UTF-8"));
StringBuilder sb = new StringBuilder();
for (byte item : array) {
sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
}
return sb.toString().toUpperCase();
} catch (Exception exception) {
}
return null;
}
}
public class JWTUtils {
/**
* 主题subject
*/
public static final String SUBJECT = "task";
/**
* 过期的时间,一周
*/
public static final long EXPIRE = 1000*60*60*24*7;
/**
* 秘钥
*/
public static final String SECRET = "tasklogin";
/**
* 令牌前缀
*/
public static final String TOKEN_PREFIX = "test";
/**
* 生成jwt
* @param user
* @return
*/
public static String geneJsonWebToken(User user){
if(user == null || user.getUserPhone() == null || user.getPassword() == null){
return null;
}
/**
* 令牌
*/
String token = Jwts.builder().setSubject(SUBJECT)
.claim("userPhone",user.getUserPhone())
.claim("password",user.getPassword())
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis()+EXPIRE))
.signWith(SignatureAlgorithm.HS256,SECRET).compact();
token = TOKEN_PREFIX + token;
return token;
}
/**
* 校验token
* @param token
* @return
*/
public static Claims checkJWT(String token ){
try{
final Claims claims = Jwts.parser().setSigningKey(SECRET).parseClaimsJws(token.replace(TOKEN_PREFIX,"")).getBody();
return claims;
}
catch (Exception e){
}
return null;
}
}
public class JsonData {
/**
* 状态码 200表示成功过,1表示处理中,-1 表示失败
*/
private Integer code;
/**
* 业务数据
*/
private Object data;
/**
* 信息表示
*/
private String msg;
public JsonData(){}
public JsonData(Integer code, Object data, String msg){
this.code = code;
this.data = data;
this.msg = msg;
}
/**
* 成功,不用返回数据
* @return
*/
public static JsonData buildSuccess(){
return new JsonData(200,null,"操作成功");
}
/**
* 成功,返回数据
* @param data
* @return
*/
public static JsonData buildSuccess(Object data){
return new JsonData(200,data,"操作成功");
}
/**
* 失败,固定状态码
* @param msg
* @return
*/
public static JsonData buildError(String msg){
return new JsonData(-1 ,null,msg);
}
/**
* 失败,自定义错误码和信息
* @param code
* @param msg
* @return
*/
public static JsonData buildError(Integer code , String msg){
return new JsonData(code ,null,msg);
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
}