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 @@