diff --git a/im-platform/src/main/java/com/bx/implatform/contant/Constant.java b/im-platform/src/main/java/com/bx/implatform/contant/Constant.java index 77fab01..fed83f1 100644 --- a/im-platform/src/main/java/com/bx/implatform/contant/Constant.java +++ b/im-platform/src/main/java/com/bx/implatform/contant/Constant.java @@ -8,5 +8,13 @@ public class Constant { public static final long MAX_FILE_SIZE = 10*1024*1024; // 群聊最大人数 public static final long MAX_GROUP_MEMBER = 500; + // accessToken 过期时间(1小时) + public static final Integer ACCESS_TOKEN_EXPIRE = 60 * 60; + // refreshToken 过期时间(7天) + public static final Integer REFRESH_TOKEN_EXPIRE = 7 * 24 * 60 * 60 ; + // accessToken 加密秘钥 + public static final String ACCESS_TOKEN_SECRET = "MIIBIjANBgkq"; + // refreshToken 加密秘钥 + public static final String REFRESH_TOKEN_SECRET = "IKDiqVmn0VFU"; } diff --git a/im-platform/src/main/java/com/bx/implatform/controller/LoginController.java b/im-platform/src/main/java/com/bx/implatform/controller/LoginController.java index c4fab21..6637130 100644 --- a/im-platform/src/main/java/com/bx/implatform/controller/LoginController.java +++ b/im-platform/src/main/java/com/bx/implatform/controller/LoginController.java @@ -1,17 +1,18 @@ package com.bx.implatform.controller; -import com.bx.implatform.enums.ResultCode; import com.bx.implatform.result.Result; import com.bx.implatform.result.ResultUtils; import com.bx.implatform.service.IUserService; +import com.bx.implatform.dto.LoginDTO; +import com.bx.implatform.dto.RegisterDTO; import com.bx.implatform.vo.LoginVO; -import com.bx.implatform.vo.RegisterVO; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RestController; import javax.validation.Valid; @@ -27,16 +28,24 @@ public class LoginController { @PostMapping("/login") @ApiOperation(value = "用户注册",notes="用户注册") - public Result register(@Valid @RequestBody LoginVO vo){ - String token = userService.login(vo); - return ResultUtils.success(token,ResultCode.SUCCESS.getMsg()); + public Result register(@Valid @RequestBody LoginDTO dto){ + LoginVO vo = userService.login(dto); + return ResultUtils.success(vo); + } + + + @PostMapping("/refreshToken") + @ApiOperation(value = "刷新token",notes="用refreshtoken换取新的token") + public Result refreshToken(@RequestHeader("refreshToken")String refreshToken){ + LoginVO vo = userService.refreshToken(refreshToken); + return ResultUtils.success(vo); } @PostMapping("/register") @ApiOperation(value = "用户注册",notes="用户注册") - public Result register(@Valid @RequestBody RegisterVO vo){ - userService.register(vo); + public Result register(@Valid @RequestBody RegisterDTO dto){ + userService.register(dto); return ResultUtils.success(); } } diff --git a/im-platform/src/main/java/com/bx/implatform/dto/LoginDTO.java b/im-platform/src/main/java/com/bx/implatform/dto/LoginDTO.java new file mode 100644 index 0000000..05a638d --- /dev/null +++ b/im-platform/src/main/java/com/bx/implatform/dto/LoginDTO.java @@ -0,0 +1,22 @@ +package com.bx.implatform.dto; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import org.hibernate.validator.constraints.Length; + +import javax.validation.constraints.NotEmpty; + +@Data +@ApiModel("用户登录VO") +public class LoginDTO { + + //@NotEmpty(message="用户名不可为空") + @ApiModelProperty(value = "用户名") + private String userName; + + // @NotEmpty(message="用户密码不可为空") + @ApiModelProperty(value = "用户密码") + private String password; + +} diff --git a/im-platform/src/main/java/com/bx/implatform/vo/RegisterVO.java b/im-platform/src/main/java/com/bx/implatform/dto/RegisterDTO.java similarity index 93% rename from im-platform/src/main/java/com/bx/implatform/vo/RegisterVO.java rename to im-platform/src/main/java/com/bx/implatform/dto/RegisterDTO.java index 0113a00..61255be 100644 --- a/im-platform/src/main/java/com/bx/implatform/vo/RegisterVO.java +++ b/im-platform/src/main/java/com/bx/implatform/dto/RegisterDTO.java @@ -1,4 +1,4 @@ -package com.bx.implatform.vo; +package com.bx.implatform.dto; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; @@ -9,7 +9,7 @@ import javax.validation.constraints.NotEmpty; @Data @ApiModel("用户注册VO") -public class RegisterVO { +public class RegisterDTO { @Length(max = 64,message = "用户名不能大于64字符") @NotEmpty(message="用户名不可为空") diff --git a/im-platform/src/main/java/com/bx/implatform/enums/ResultCode.java b/im-platform/src/main/java/com/bx/implatform/enums/ResultCode.java index 2a1f5b1..74981f6 100644 --- a/im-platform/src/main/java/com/bx/implatform/enums/ResultCode.java +++ b/im-platform/src/main/java/com/bx/implatform/enums/ResultCode.java @@ -9,8 +9,8 @@ package com.bx.implatform.enums; **/ public enum ResultCode { SUCCESS(200,"成功"), - LOGIN_ERROR(400,"登录异常"), - NO_LOGIN(401,"未登录"), + NO_LOGIN(400,"未登录"), + INVALID_TOKEN(401,"token已失效"), PROGRAM_ERROR(500,"系统繁忙,请稍后再试"), PASSWOR_ERROR(10001,"密码不正确"), USERNAME_ALREADY_REGISTER(10003,"该用户名已注册"), diff --git a/im-platform/src/main/java/com/bx/implatform/interceptor/AuthInterceptor.java b/im-platform/src/main/java/com/bx/implatform/interceptor/AuthInterceptor.java index edea946..d1f275a 100644 --- a/im-platform/src/main/java/com/bx/implatform/interceptor/AuthInterceptor.java +++ b/im-platform/src/main/java/com/bx/implatform/interceptor/AuthInterceptor.java @@ -2,6 +2,8 @@ package com.bx.implatform.interceptor; import com.alibaba.fastjson.JSON; +import com.auth0.jwt.exceptions.JWTVerificationException; +import com.bx.implatform.contant.Constant; import com.bx.implatform.enums.ResultCode; import com.bx.implatform.exception.GlobalException; import com.bx.implatform.session.UserSession; @@ -26,8 +28,13 @@ public class AuthInterceptor implements HandlerInterceptor { if (token == null) { throw new GlobalException(ResultCode.NO_LOGIN); } - //验证 token - JwtUtil.checkSign(token); + try{ + //验证 token + JwtUtil.checkSign(token, Constant.ACCESS_TOKEN_SECRET); + }catch ( + JWTVerificationException e) { + throw new GlobalException(ResultCode.INVALID_TOKEN); + } // 存放session String strJson = JwtUtil.getInfo(token); UserSession userSession = JSON.parseObject(strJson,UserSession.class); diff --git a/im-platform/src/main/java/com/bx/implatform/service/IUserService.java b/im-platform/src/main/java/com/bx/implatform/service/IUserService.java index 5706b42..4e48398 100644 --- a/im-platform/src/main/java/com/bx/implatform/service/IUserService.java +++ b/im-platform/src/main/java/com/bx/implatform/service/IUserService.java @@ -2,8 +2,9 @@ package com.bx.implatform.service; import com.baomidou.mybatisplus.extension.service.IService; import com.bx.implatform.entity.User; +import com.bx.implatform.dto.LoginDTO; +import com.bx.implatform.dto.RegisterDTO; import com.bx.implatform.vo.LoginVO; -import com.bx.implatform.vo.RegisterVO; import com.bx.implatform.vo.UserVO; import java.util.List; @@ -11,9 +12,11 @@ import java.util.List; public interface IUserService extends IService { - String login(LoginVO vo); + LoginVO login(LoginDTO dto); - void register(RegisterVO vo); + LoginVO refreshToken(String refreshToken); + + void register(RegisterDTO dto); User findUserByName(String username); diff --git a/im-platform/src/main/java/com/bx/implatform/service/impl/UserServiceImpl.java b/im-platform/src/main/java/com/bx/implatform/service/impl/UserServiceImpl.java index 0f6d0db..d522974 100644 --- a/im-platform/src/main/java/com/bx/implatform/service/impl/UserServiceImpl.java +++ b/im-platform/src/main/java/com/bx/implatform/service/impl/UserServiceImpl.java @@ -1,9 +1,11 @@ package com.bx.implatform.service.impl; import com.alibaba.fastjson.JSON; +import com.auth0.jwt.exceptions.JWTVerificationException; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.bx.imcommon.contant.RedisKey; +import com.bx.implatform.contant.Constant; import com.bx.implatform.entity.Friend; import com.bx.implatform.entity.GroupMember; import com.bx.implatform.entity.User; @@ -17,8 +19,9 @@ import com.bx.implatform.session.SessionContext; import com.bx.implatform.session.UserSession; import com.bx.implatform.util.BeanUtils; import com.bx.implatform.util.JwtUtil; +import com.bx.implatform.dto.LoginDTO; +import com.bx.implatform.dto.RegisterDTO; import com.bx.implatform.vo.LoginVO; -import com.bx.implatform.vo.RegisterVO; import com.bx.implatform.vo.UserVO; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; @@ -31,6 +34,8 @@ import java.util.LinkedList; import java.util.List; import java.util.stream.Collectors; +import static com.bx.implatform.contant.Constant.REFRESH_TOKEN_SECRET; + @Slf4j @Service @@ -51,42 +56,74 @@ public class UserServiceImpl extends ServiceImpl implements IU /** * 用户登录 * - * @param vo 注册vo + * @param dto 登录dto * @return */ @Override - public String login(LoginVO vo) { - User user = findUserByName(vo.getUserName()); + public LoginVO login(LoginDTO dto) { + User user = findUserByName(dto.getUserName()); if(null == user){ throw new GlobalException(ResultCode.PROGRAM_ERROR,"用户不存在"); } - if(!passwordEncoder.matches(vo.getPassword(),user.getPassword())){ + if(!passwordEncoder.matches(dto.getPassword(),user.getPassword())){ throw new GlobalException(ResultCode.PASSWOR_ERROR); } // 生成token UserSession session = BeanUtils.copyProperties(user,UserSession.class); String strJson = JSON.toJSONString(session); - String token = JwtUtil.sign(user.getId(),strJson); - return token; + String accessToken = JwtUtil.sign(user.getId(),strJson, Constant.ACCESS_TOKEN_EXPIRE,Constant.ACCESS_TOKEN_SECRET); + String refreshToken = JwtUtil.sign(user.getId(),strJson, Constant.REFRESH_TOKEN_EXPIRE, REFRESH_TOKEN_SECRET); + LoginVO vo =new LoginVO(); + vo.setAccessToken(accessToken); + vo.setAccessTokenExpiresIn(Constant.ACCESS_TOKEN_EXPIRE); + vo.setRefreshToken(refreshToken); + vo.setAccessTokenExpiresIn(Constant.REFRESH_TOKEN_EXPIRE); + return vo; + } + + /** + * 用refreshToken换取新 token + * + * @param refreshToken + * @return + */ + @Override + public LoginVO refreshToken(String refreshToken) { + try{ + //验证 token + JwtUtil.checkSign(refreshToken, REFRESH_TOKEN_SECRET); + String strJson = JwtUtil.getInfo(refreshToken); + Long userId = JwtUtil.getUserId(refreshToken); + String accessToken = JwtUtil.sign(userId,strJson, Constant.ACCESS_TOKEN_EXPIRE,Constant.ACCESS_TOKEN_SECRET); + String newRefreshToken = JwtUtil.sign(userId,strJson, Constant.REFRESH_TOKEN_EXPIRE, REFRESH_TOKEN_SECRET); + LoginVO vo =new LoginVO(); + vo.setAccessToken(accessToken); + vo.setAccessTokenExpiresIn(Constant.ACCESS_TOKEN_EXPIRE); + vo.setRefreshToken(newRefreshToken); + vo.setAccessTokenExpiresIn(Constant.REFRESH_TOKEN_EXPIRE); + return vo; + }catch (JWTVerificationException e) { + throw new GlobalException(ResultCode.INVALID_TOKEN); + } } /** * 用户注册 * - * @param vo 注册vo + * @param dto 注册dto * @return */ @Override - public void register(RegisterVO vo) { - User user = findUserByName(vo.getUserName()); + public void register(RegisterDTO dto) { + User user = findUserByName(dto.getUserName()); if(null != user){ throw new GlobalException(ResultCode.USERNAME_ALREADY_REGISTER); } - user = BeanUtils.copyProperties(vo,User.class); + user = BeanUtils.copyProperties(dto,User.class); user.setPassword(passwordEncoder.encode(user.getPassword())); this.save(user); - log.info("注册用户,用户id:{},用户名:{},昵称:{}",user.getId(),vo.getUserName(),vo.getNickName()); + log.info("注册用户,用户id:{},用户名:{},昵称:{}",user.getId(),dto.getUserName(),dto.getNickName()); } /** diff --git a/im-platform/src/main/java/com/bx/implatform/util/JwtUtil.java b/im-platform/src/main/java/com/bx/implatform/util/JwtUtil.java index 2c27a83..33f29b3 100644 --- a/im-platform/src/main/java/com/bx/implatform/util/JwtUtil.java +++ b/im-platform/src/main/java/com/bx/implatform/util/JwtUtil.java @@ -4,34 +4,23 @@ import com.auth0.jwt.JWT; import com.auth0.jwt.JWTVerifier; import com.auth0.jwt.algorithms.Algorithm; import com.auth0.jwt.exceptions.JWTDecodeException; -import com.auth0.jwt.exceptions.JWTVerificationException; -import com.bx.implatform.exception.GlobalException; -; - import java.util.Date; -import java.util.Map; -public class JwtUtil { - /** - * 过期5分钟 - * */ - private static final long EXPIRE_TIME = 30 * 60 * 1000; - /** - * jwt密钥 - * */ - private static final String SECRET = "MIIBIjANBgkq"; +public class JwtUtil { /** * 生成jwt字符串,30分钟后过期 JWT(json web token) * @param userId * @param info + * @param expireIn + * @param secret * @return * */ - public static String sign(Long userId, String info) { + public static String sign(Long userId, String info,long expireIn,String secret) { try { - Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME); - Algorithm algorithm = Algorithm.HMAC256(SECRET); + Date date = new Date(System.currentTimeMillis() + expireIn*1000); + Algorithm algorithm = Algorithm.HMAC256(secret); return JWT.create() //将userId保存到token里面 .withAudience(userId.toString()) @@ -77,18 +66,15 @@ public class JwtUtil { /** * 校验token * @param token + * @param secret * @return * */ - public static boolean checkSign(String token) { - try { - Algorithm algorithm = Algorithm.HMAC256(SECRET); + public static boolean checkSign(String token,String secret) { + Algorithm algorithm = Algorithm.HMAC256(secret); JWTVerifier verifier = JWT.require(algorithm) //.withClaim("username, username) .build(); verifier.verify(token); return true; - }catch (JWTVerificationException e) { - throw new GlobalException("token 无效,请重新获取"); - } } } diff --git a/im-platform/src/main/java/com/bx/implatform/vo/LoginVO.java b/im-platform/src/main/java/com/bx/implatform/vo/LoginVO.java index ad2a841..500bb43 100644 --- a/im-platform/src/main/java/com/bx/implatform/vo/LoginVO.java +++ b/im-platform/src/main/java/com/bx/implatform/vo/LoginVO.java @@ -3,20 +3,20 @@ package com.bx.implatform.vo; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; -import org.hibernate.validator.constraints.Length; - -import javax.validation.constraints.NotEmpty; @Data @ApiModel("用户登录VO") public class LoginVO { - //@NotEmpty(message="用户名不可为空") - @ApiModelProperty(value = "用户名") - private String userName; + @ApiModelProperty(value = "每次请求都必须在header中携带accessToken") + private String accessToken; + + @ApiModelProperty(value = "accessToken过期时间(秒)") + private Integer accessTokenExpiresIn; - // @NotEmpty(message="用户密码不可为空") - @ApiModelProperty(value = "用户密码") - private String password; + @ApiModelProperty(value = "accessToken过期后,通过refreshToken换取新的token") + private String refreshToken; + @ApiModelProperty(value = "refreshToken过期时间(秒)") + private Integer refreshTokenExpiresIn; } diff --git a/im-ui/src/view/Login.vue b/im-ui/src/view/Login.vue index 45ff95a..0ef70cb 100644 --- a/im-ui/src/view/Login.vue +++ b/im-ui/src/view/Login.vue @@ -66,12 +66,12 @@ method: 'post', data: this.loginForm }) - .then((token) => { + .then((data) => { // 保存密码到cookie(方便但不安全) this.setCookie('username',this.loginForm.userName); this.setCookie('password',this.loginForm.password); // 保存token - sessionStorage.setItem("token",token); + sessionStorage.setItem("token",data.accessToken); this.$message.success("登陆成功"); this.$router.push("/home/chat"); })