diff --git a/im-platform/pom.xml b/im-platform/pom.xml
index c8309c9..c6c3dc5 100644
--- a/im-platform/pom.xml
+++ b/im-platform/pom.xml
@@ -106,6 +106,12 @@
mybatis-plus-generator
3.3.2
+
+
+ com.auth0
+ java-jwt
+ 3.11.0
+
diff --git a/im-platform/src/main/java/com/bx/implatform/ImplatformApp.java b/im-platform/src/main/java/com/bx/implatform/ImplatformApp.java
index 15480c2..baf7338 100644
--- a/im-platform/src/main/java/com/bx/implatform/ImplatformApp.java
+++ b/im-platform/src/main/java/com/bx/implatform/ImplatformApp.java
@@ -4,13 +4,14 @@ import lombok.extern.slf4j.Slf4j;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Slf4j
@EnableAspectJAutoProxy(exposeProxy = true)
@MapperScan(basePackages = {"com.bx.implatform.mapper"})
-@SpringBootApplication
+@SpringBootApplication(exclude= {SecurityAutoConfiguration.class })// 禁用secrity
public class ImplatformApp {
public static void main(String[] args) {
diff --git a/im-platform/src/main/java/com/bx/implatform/config/MvcConfig.java b/im-platform/src/main/java/com/bx/implatform/config/MvcConfig.java
new file mode 100644
index 0000000..ccf7192
--- /dev/null
+++ b/im-platform/src/main/java/com/bx/implatform/config/MvcConfig.java
@@ -0,0 +1,36 @@
+package com.bx.implatform.config;
+
+import com.bx.implatform.interceptor.AuthInterceptor;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+
+@Configuration
+public class MvcConfig implements WebMvcConfigurer {
+
+
+ @Override
+ public void addInterceptors(InterceptorRegistry registry) {
+ registry.addInterceptor(authInterceptor())
+ .addPathPatterns("/**")
+ .excludePathPatterns( "/image/upload","/login","/logout","/register","/refreshToken",
+ "/swagger-resources/**", "/webjars/**", "/v2/**", "/swagger-ui.html/**");
+ }
+
+
+ @Bean
+ public AuthInterceptor authInterceptor() {
+ return new AuthInterceptor();
+ }
+
+ @Bean
+ public PasswordEncoder passwordEncoder(){
+ // 使用BCrypt加密密码
+ return new BCryptPasswordEncoder();
+ }
+
+}
diff --git a/im-platform/src/main/java/com/bx/implatform/config/WebSecurityConfg.java b/im-platform/src/main/java/com/bx/implatform/config/WebSecurityConfg.java
deleted file mode 100644
index 7ef0d62..0000000
--- a/im-platform/src/main/java/com/bx/implatform/config/WebSecurityConfg.java
+++ /dev/null
@@ -1,172 +0,0 @@
-package com.bx.implatform.config;
-
-import com.alibaba.fastjson.JSON;
-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.session.UserSession;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Qualifier;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.security.authentication.*;
-import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
-import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
-import org.springframework.security.config.annotation.web.builders.HttpSecurity;
-import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
-import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
-import org.springframework.security.core.userdetails.User;
-import org.springframework.security.core.userdetails.UserDetailsService;
-import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
-import org.springframework.security.crypto.password.PasswordEncoder;
-import org.springframework.security.web.AuthenticationEntryPoint;
-import org.springframework.security.web.authentication.AuthenticationFailureHandler;
-import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
-import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
-
-import java.io.PrintWriter;
-
-/*
- * SpringSecurity安全框架配置
- *
- * @Author Blue
- * @Date 2022/10/21
- */
-@Slf4j
-@Configuration
-@EnableWebSecurity
-@EnableGlobalMethodSecurity(prePostEnabled=true)
-public class WebSecurityConfg extends WebSecurityConfigurerAdapter {
-
-
-
- @Qualifier("securityUserDetailsServiceImpl")
- @Autowired
- private UserDetailsService userDetailsService;
-
- @Autowired
- private IUserService userService;
-
- @Override
- protected void configure(HttpSecurity http) throws Exception {
- http.authorizeRequests()
- .antMatchers("/image/upload","/login","/logout","/register","/swagger-resources/**", "/webjars/**", "/v2/**", "/swagger-ui.html/**")
- .permitAll()
- .anyRequest() //任何其它请求
- .authenticated() //都需要身份认证
- .and()
- // 登录配置表单认证方式
- .formLogin()
- .usernameParameter("username")//设置登录账号参数,与表单参数一致
- .passwordParameter("password")//设置登录密码参数,与表单参数一致
- .loginProcessingUrl("/login")//配置默认登录入口
- .successHandler(successHandler())
- .failureHandler(failureHandler())
- .and()
- // 注销
- .logout()
- .logoutUrl("/logout")
- .logoutSuccessHandler(logoutHandler())
- .permitAll()
- .and()
- // session管理
- .sessionManagement()
- .and()
- // 禁用跨站csrf攻击防御
- .csrf()
- .disable()
- .exceptionHandling()
- .authenticationEntryPoint(entryPoint());
-
- }
-
- @Bean
- AuthenticationFailureHandler failureHandler(){
- return (request, response, exception) -> {
- response.setContentType("application/json;charset=utf-8");
- PrintWriter out = response.getWriter();
- Result result = ResultUtils.error(ResultCode.LOGIN_ERROR,exception.getMessage());
- if (exception instanceof LockedException) {
- result =ResultUtils.error(ResultCode.LOGIN_ERROR,"账户被锁定,请联系管理员!");
- } else if (exception instanceof CredentialsExpiredException) {
- result = ResultUtils.error(ResultCode.LOGIN_ERROR,"密码过期,请联系管理员!");
- } else if (exception instanceof AccountExpiredException) {
- result =ResultUtils.error(ResultCode.LOGIN_ERROR,"账户过期,请联系管理员!");
- } else if (exception instanceof DisabledException) {
- result = ResultUtils.error(ResultCode.LOGIN_ERROR,"账户被禁用,请联系管理员!");
- } else if (exception instanceof BadCredentialsException) {
- result =ResultUtils.error(ResultCode.LOGIN_ERROR,"用户名或者密码输入错误,请重新输入!");
- }
- out.write(new ObjectMapper().writeValueAsString(result));
- out.flush();
- out.close();
- };
- }
-
- @Bean
- AuthenticationSuccessHandler successHandler(){
- return (request, response, authentication) -> {
- User useDetail = (User)authentication.getPrincipal();
- String strJson = useDetail.getUsername();
- UserSession userSession = JSON.parseObject(strJson,UserSession.class);
- log.info("用户 '{}' 登录,id:{},昵称:{}",userSession.getUserName(),userSession.getId(),userSession.getNickName());
- // 响应
- response.setContentType("application/json;charset=utf-8");
- PrintWriter out = response.getWriter();
- Result result = ResultUtils.success();
- out.write(new ObjectMapper().writeValueAsString(result));
- out.flush();
- out.close();
-
- };
- }
-
-
- @Bean
- LogoutSuccessHandler logoutHandler(){
- return (request, response, authentication) -> {
- User useDetail = (User)authentication.getPrincipal();
- String strJson = useDetail.getUsername();
- UserSession userSession = JSON.parseObject(strJson,UserSession.class);
- log.info("用户 '{}' 退出,id:{},昵称:{}",userSession.getUserName(),userSession.getId(),userSession.getNickName());
- // 响应
- response.setContentType("application/json;charset=utf-8");
- PrintWriter out = response.getWriter();
- Result result = ResultUtils.success();
- out.write(new ObjectMapper().writeValueAsString(result));
- out.flush();
- out.close();
- };
- }
-
- @Bean
- AuthenticationEntryPoint entryPoint(){
- return (request, response, exception) -> {
- response.setContentType("application/json;charset=utf-8");
- PrintWriter out = response.getWriter();
- Result result = ResultUtils.error(ResultCode.NO_LOGIN);
- out.write(new ObjectMapper().writeValueAsString(result));
- out.flush();
- out.close();
- };
- }
-
-
-
- @Bean
- public PasswordEncoder passwordEncoder(){
- // 使用BCrypt加密密码
- return new BCryptPasswordEncoder();
- }
-
-
- @Override
- protected void configure(AuthenticationManagerBuilder auth) throws Exception {
- auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
- }
-
-
-}
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..4aedad3 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 = 30 * 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
new file mode 100644
index 0000000..4e70554
--- /dev/null
+++ b/im-platform/src/main/java/com/bx/implatform/controller/LoginController.java
@@ -0,0 +1,48 @@
+package com.bx.implatform.controller;
+
+
+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 io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import javax.validation.Valid;
+
+
+
+@Api(tags = "用户登录和注册")
+@RestController
+public class LoginController {
+
+ @Autowired
+ private IUserService userService;
+
+ @PostMapping("/login")
+ @ApiOperation(value = "用户注册",notes="用户注册")
+ public Result register(@Valid @RequestBody LoginDTO dto){
+ LoginVO vo = userService.login(dto);
+ return ResultUtils.success(vo);
+ }
+
+
+ @PutMapping("/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 RegisterDTO dto){
+ userService.register(dto);
+ return ResultUtils.success();
+ }
+}
diff --git a/im-platform/src/main/java/com/bx/implatform/controller/RegisterController.java b/im-platform/src/main/java/com/bx/implatform/controller/RegisterController.java
deleted file mode 100644
index a1250b8..0000000
--- a/im-platform/src/main/java/com/bx/implatform/controller/RegisterController.java
+++ /dev/null
@@ -1,32 +0,0 @@
-package com.bx.implatform.controller;
-
-
-import com.bx.implatform.result.Result;
-import com.bx.implatform.result.ResultUtils;
-import com.bx.implatform.service.IUserService;
-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.RestController;
-
-import javax.validation.Valid;
-
-
-
-@Api(tags = "用户注册")
-@RestController
-public class RegisterController {
-
- @Autowired
- private IUserService userService;
-
- @PostMapping("/register")
- @ApiOperation(value = "用户注册",notes="用户注册")
- public Result register(@Valid @RequestBody RegisterVO 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 bc6f96c..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,16 +9,11 @@ package com.bx.implatform.enums;
**/
public enum ResultCode {
SUCCESS(200,"成功"),
- LOGIN_ERROR(400,"登录异常"),
- NO_LOGIN(401,"未登录"),
- FORBIDDEN(403,"禁止访问"),
- NOT_FIND(404,"无法找到文件"),
+ NO_LOGIN(400,"未登录"),
+ INVALID_TOKEN(401,"token已失效"),
PROGRAM_ERROR(500,"系统繁忙,请稍后再试"),
PASSWOR_ERROR(10001,"密码不正确"),
- VERITY_CODE_NOT_EXIST(10002,"验证码不存在"),
- VERITY_CODE_ERROR(10003,"验证码不正确"),
- USERNAME_ALREADY_REGISTER(10004,"该用户名已注册"),
- MOBILE_ALREADY_REGISTER(10005,"该手机号码已注册"),
+ 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
new file mode 100644
index 0000000..064fc22
--- /dev/null
+++ b/im-platform/src/main/java/com/bx/implatform/interceptor/AuthInterceptor.java
@@ -0,0 +1,48 @@
+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;
+import com.bx.implatform.util.JwtUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.method.HandlerMethod;
+import org.springframework.web.servlet.HandlerInterceptor;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+;
+
+@Slf4j
+public class AuthInterceptor implements HandlerInterceptor {
+
+
+ @Override
+ public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
+ //如果不是映射到方法直接通过
+ if (!(handler instanceof HandlerMethod)) {
+ return true;
+ }
+ //从 http 请求头中取出 token
+ String token = request.getHeader("accessToken");
+ if (token == null) {
+ log.error("未登陆,url:{}",request.getRequestURI());
+ throw new GlobalException(ResultCode.NO_LOGIN);
+ }
+ try{
+ //验证 token
+ JwtUtil.checkSign(token, Constant.ACCESS_TOKEN_SECRET);
+ }catch (
+ JWTVerificationException e) {
+ log.error("token已失效,url:{}",request.getRequestURI());
+ throw new GlobalException(ResultCode.INVALID_TOKEN);
+ }
+ // 存放session
+ String strJson = JwtUtil.getInfo(token);
+ UserSession userSession = JSON.parseObject(strJson,UserSession.class);
+ request.setAttribute("session",userSession);
+ return true;
+ }
+}
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 b45d43f..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,7 +2,9 @@ package com.bx.implatform.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.bx.implatform.entity.User;
-import com.bx.implatform.vo.RegisterVO;
+import com.bx.implatform.dto.LoginDTO;
+import com.bx.implatform.dto.RegisterDTO;
+import com.bx.implatform.vo.LoginVO;
import com.bx.implatform.vo.UserVO;
import java.util.List;
@@ -10,7 +12,11 @@ import java.util.List;
public interface IUserService extends IService {
- void register(RegisterVO registerDTO);
+ LoginVO login(LoginDTO dto);
+
+ 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 644b80b..b04aebf 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,8 +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;
@@ -15,7 +18,10 @@ import com.bx.implatform.service.IUserService;
import com.bx.implatform.session.SessionContext;
import com.bx.implatform.session.UserSession;
import com.bx.implatform.util.BeanUtils;
-import com.bx.implatform.vo.RegisterVO;
+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.UserVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
@@ -28,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
@@ -45,22 +53,77 @@ public class UserServiceImpl extends ServiceImpl implements IU
@Autowired
private IFriendService friendService;
+ /**
+ * 用户登录
+ *
+ * @param dto 登录dto
+ * @return
+ */
+
+ @Override
+ public LoginVO login(LoginDTO dto) {
+ User user = findUserByName(dto.getUserName());
+ if(null == user){
+ throw new GlobalException(ResultCode.PROGRAM_ERROR,"用户不存在");
+ }
+ 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 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.setRefreshTokenExpiresIn(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.setRefreshTokenExpiresIn(Constant.REFRESH_TOKEN_EXPIRE);
+ return vo;
+ }catch (JWTVerificationException e) {
+ throw new GlobalException("refreshToken已失效");
+ }
+ }
+
/**
* 用户注册
*
- * @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/session/SessionContext.java b/im-platform/src/main/java/com/bx/implatform/session/SessionContext.java
index d21cf81..b9a0c63 100644
--- a/im-platform/src/main/java/com/bx/implatform/session/SessionContext.java
+++ b/im-platform/src/main/java/com/bx/implatform/session/SessionContext.java
@@ -1,8 +1,9 @@
package com.bx.implatform.session;
-import com.alibaba.fastjson.JSON;
-import org.springframework.security.core.context.SecurityContextHolder;
-import org.springframework.security.core.userdetails.User;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import javax.servlet.http.HttpServletRequest;
/*
* @Description
@@ -13,11 +14,12 @@ public class SessionContext {
public static UserSession getSession(){
- User useDetail = (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
- String strJson = useDetail.getUsername();
- UserSession userSession = JSON.parseObject(strJson,UserSession.class);
+ // 从请求上下文里获取Request对象
+ ServletRequestAttributes requestAttributes = ServletRequestAttributes.class.
+ cast(RequestContextHolder.getRequestAttributes());
+ HttpServletRequest request = requestAttributes.getRequest();
+ UserSession userSession = (UserSession) request.getAttribute("session");
return userSession;
}
-
}
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
new file mode 100644
index 0000000..33f29b3
--- /dev/null
+++ b/im-platform/src/main/java/com/bx/implatform/util/JwtUtil.java
@@ -0,0 +1,80 @@
+package com.bx.implatform.util;
+
+import com.auth0.jwt.JWT;
+import com.auth0.jwt.JWTVerifier;
+import com.auth0.jwt.algorithms.Algorithm;
+import com.auth0.jwt.exceptions.JWTDecodeException;
+import java.util.Date;
+
+
+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,long expireIn,String secret) {
+ try {
+ Date date = new Date(System.currentTimeMillis() + expireIn*1000);
+ Algorithm algorithm = Algorithm.HMAC256(secret);
+ return JWT.create()
+ //将userId保存到token里面
+ .withAudience(userId.toString())
+ //存放自定义数据
+ .withClaim("info", info)
+ //五分钟后token过期
+ .withExpiresAt(date)
+ //token的密钥
+ .sign(algorithm);
+ } catch (Exception e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ /**
+ * 根据token获取userId
+ * @param token
+ * @return
+ * */
+ public static Long getUserId(String token) {
+ try {
+ String userId = JWT.decode(token).getAudience().get(0);
+ return Long.parseLong(userId);
+ }catch (JWTDecodeException e) {
+ return null;
+ }
+ }
+
+ /**
+ * 根据token获取自定义数据info
+ * @param token
+ * @return
+ * */
+ public static String getInfo(String token) {
+ try {
+ return JWT.decode(token).getClaim("info").asString();
+ }catch (JWTDecodeException e) {
+ return null;
+ }
+ }
+
+ /**
+ * 校验token
+ * @param token
+ * @param secret
+ * @return
+ * */
+ 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;
+ }
+}
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
new file mode 100644
index 0000000..500bb43
--- /dev/null
+++ b/im-platform/src/main/java/com/bx/implatform/vo/LoginVO.java
@@ -0,0 +1,22 @@
+package com.bx.implatform.vo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+@Data
+@ApiModel("用户登录VO")
+public class LoginVO {
+
+ @ApiModelProperty(value = "每次请求都必须在header中携带accessToken")
+ private String accessToken;
+
+ @ApiModelProperty(value = "accessToken过期时间(秒)")
+ private Integer accessTokenExpiresIn;
+
+ @ApiModelProperty(value = "accessToken过期后,通过refreshToken换取新的token")
+ private String refreshToken;
+
+ @ApiModelProperty(value = "refreshToken过期时间(秒)")
+ private Integer refreshTokenExpiresIn;
+}
diff --git a/im-ui/src/api/httpRequest.js b/im-ui/src/api/httpRequest.js
index bd93ec1..7f8f376 100644
--- a/im-ui/src/api/httpRequest.js
+++ b/im-ui/src/api/httpRequest.js
@@ -1,6 +1,8 @@
import axios from 'axios'
import router from '@/router'
-import {Message} from 'element-ui'
+import {
+ Message
+} from 'element-ui'
const http = axios.create({
baseURL: process.env.VUE_APP_BASE_API,
@@ -15,7 +17,10 @@ const http = axios.create({
* 请求拦截
*/
http.interceptors.request.use(config => {
- // todo 请求头带上token
+ let accessToken = sessionStorage.getItem("accessToken");
+ if (accessToken) {
+ config.headers.accessToken = encodeURIComponent(accessToken);
+ }
return config
}, error => {
return Promise.reject(error)
@@ -24,9 +29,32 @@ http.interceptors.request.use(config => {
/**
* 响应拦截
*/
-http.interceptors.response.use(response => {
+http.interceptors.response.use(async response => {
if (response.data.code == 200) {
return response.data.data;
+ } else if (response.data.code == 400) {
+ router.replace("/login");
+ } else if (response.data.code == 401) {
+ console.log("token失效,尝试重新获取")
+ let refreshToken = sessionStorage.getItem("refreshToken");
+ if (!refreshToken) {
+ router.replace("/login");
+ }
+ // 发送请求, 进行刷新token操作, 获取新的token
+ const data = await http({
+ method: 'put',
+ url: '/refreshToken',
+ headers: {
+ refreshToken: refreshToken
+ }
+ })
+ // 保存token
+ sessionStorage.setItem("accessToken", data.accessToken);
+ sessionStorage.setItem("refreshToken", data.refreshToken);
+ // 这里需要把headers清掉,否则请求时会报错,原因暂不详...
+ response.config.headers=undefined;
+ // 重新发送刚才的请求
+ return http(response.config)
} else {
Message({
message: response.data.message,
@@ -34,10 +62,6 @@ http.interceptors.response.use(response => {
duration: 1500,
customClass: 'element-error-message-zindex'
})
-
- if (response.data.code == 401) {
- router.replace("/login");
- }
return Promise.reject(response.data)
}
}, error => {
diff --git a/im-ui/src/view/Home.vue b/im-ui/src/view/Home.vue
index a3b8b89..06359c1 100644
--- a/im-ui/src/view/Home.vue
+++ b/im-ui/src/view/Home.vue
@@ -190,13 +190,9 @@
this.playAudioTip();
},
handleExit() {
- this.$http({
- url: "/logout",
- method: 'get'
- }).then(() => {
- this.$wsApi.closeWebSocket();
- location.href = "/";
- })
+ this.$wsApi.closeWebSocket();
+ sessionStorage.removeItem("token");
+ location.href = "/";
},
playAudioTip(){
let audio = new Audio();
diff --git a/im-ui/src/view/Login.vue b/im-ui/src/view/Login.vue
index c25fa33..620bb49 100644
--- a/im-ui/src/view/Login.vue
+++ b/im-ui/src/view/Login.vue
@@ -2,8 +2,8 @@
欢迎登陆
-
-
+
+
@@ -42,11 +42,11 @@
};
return {
loginForm: {
- username: '',
+ userName: '',
password: ''
},
rules: {
- username: [{
+ userName: [{
validator: checkUsername,
trigger: 'blur'
}],
@@ -64,11 +64,15 @@
this.$http({
url: "/login",
method: 'post',
- params: this.loginForm
+ data: this.loginForm
})
.then((data) => {
- this.setCookie('username',this.loginForm.username);
+ // 保存密码到cookie(不安全)
+ this.setCookie('username',this.loginForm.userName);
this.setCookie('password',this.loginForm.password);
+ // 保存token
+ sessionStorage.setItem("accessToken",data.accessToken);
+ sessionStorage.setItem("refreshToken",data.refreshToken);
this.$message.success("登陆成功");
this.$router.push("/home/chat");
})
@@ -105,7 +109,7 @@
}
},
mounted() {
- this.loginForm.username = this.getCookie("username");
+ this.loginForm.userName = this.getCookie("username");
// cookie存密码并不安全,暂时是为了方便
this.loginForm.password = this.getCookie("password");
}