package com.mzl.flower.web.login; import cn.hutool.core.util.StrUtil; import com.mzl.flower.base.BaseController; import com.mzl.flower.base.R; import com.mzl.flower.base.ReturnDataDTO; import com.mzl.flower.base.cache.StringCacheClient; import com.mzl.flower.config.exception.BaseException; import com.mzl.flower.config.exception.ValidationException; import com.mzl.flower.config.security.SecurityUtils; import com.mzl.flower.config.security.token.AdminAuthenticationToken; import com.mzl.flower.config.security.token.PhoneAuthenticationToken; import com.mzl.flower.config.security.token.UserIdAuthenticationToken; import com.mzl.flower.constant.Constants; import com.mzl.flower.dto.request.RefreshTokenDto; import com.mzl.flower.dto.request.UserLoginDTO; import com.mzl.flower.dto.security.UserDTO; import com.mzl.flower.entity.system.User; import com.mzl.flower.service.login.LoginService; import com.mzl.flower.service.system.UserService; import com.wf.captcha.GifCaptcha; import com.wf.captcha.utils.CaptchaUtil; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpHeaders; import org.springframework.http.ResponseEntity; import org.springframework.security.authentication.AuthenticationDetailsSource; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.core.Authentication; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.oauth2.common.OAuth2AccessToken; import org.springframework.security.oauth2.common.OAuth2RefreshToken; import org.springframework.security.oauth2.common.exceptions.InvalidGrantException; import org.springframework.security.oauth2.provider.OAuth2Authentication; import org.springframework.security.oauth2.provider.token.TokenStore; import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @Slf4j @RestController @RequestMapping("/api") @Api(value = "运营平台-登录", tags = "登录-登录") public class LoginController extends BaseController { private AuthenticationDetailsSource authenticationDetailsSource = new WebAuthenticationDetailsSource(); @Autowired private AuthenticationManager authenticationManager; @Autowired private TokenStore tokenStore; @Autowired private LoginService loginService; @Autowired private StringCacheClient stringCacheClient; @Autowired private UserService userService; public static final String SMS_CODE_KEY = "SMS-CODE-KEY"; public static final String TOKEN_KEY = "TOKEN-KEY"; public static final String SEPARATOR = ":"; @PostMapping("/login/admin") @ApiOperation(value = "管理员登录", notes = "管理员登录") public ResponseEntity> login(HttpServletRequest request , @RequestBody UserLoginDTO loginDTO) { checkCaptchaCode(loginDTO.getCodeId(), loginDTO.getCode()); String username = loginDTO.getUsername(); String password = loginDTO.getPassword(); if (StringUtils.isBlank(username)) { throw new ValidationException("用户名不能为空"); } if (StringUtils.isBlank(password)) { throw new ValidationException("密码不能为空"); } User user = userService.findByTel(username, Constants.USER_TYPE.admin.name()); if(user == null){ throw new ValidationException("用户不存在"); } String tokenCache = stringCacheClient.get(TOKEN_KEY + SEPARATOR + user.getId()); if (StringUtils.isNotBlank(tokenCache)) { //强制删除token,下线 removeToken(tokenCache,user.getId()); } try { AdminAuthenticationToken authRequest = new AdminAuthenticationToken(username, password); authRequest.setDetails(authenticationDetailsSource.buildDetails(request)); Authentication authentication = authenticationManager.authenticate(authRequest); OAuth2AccessToken token = loginService.getAccessToken(authentication,Constants.USER_TYPE.admin.name()); stringCacheClient.set(TOKEN_KEY + SEPARATOR + user.getId(),token.getValue()); return returnData(R.SUCCESS.getCode(),token); }catch (BadCredentialsException e){ throw new ValidationException("用户名或密码错误"); }catch (UsernameNotFoundException e){ throw new ValidationException("用户不存在"); }catch (Exception e) { log.error(e.getMessage(), e); throw new BaseException(R.RUNTIME_EXCEPTION.getCode(),"登录错误"); } } @PostMapping("/login/admin/wx") @ApiOperation(value = "微信端-登录", notes = "微信端-登录") public ResponseEntity> loginWxAdmin(HttpServletRequest request , @RequestHeader(value = "clientType", required = false) String clientType , @RequestBody UserLoginDTO loginDTO) { String username = loginDTO.getUsername(); String password = loginDTO.getPassword(); if (StringUtils.isBlank(username)) { throw new ValidationException("用户名不能为空"); } if (StringUtils.isBlank(password)) { throw new ValidationException("密码不能为空"); } User user = userService.findByTel(username, Constants.USER_TYPE.admin.name()); if(user == null){ throw new ValidationException("用户不存在"); } String tokenCache = stringCacheClient.get(TOKEN_KEY + SEPARATOR + user.getId()); if (StringUtils.isNotBlank(tokenCache)) { //强制删除token,下线 removeToken(tokenCache,user.getId()); } try { AdminAuthenticationToken authRequest = new AdminAuthenticationToken(username, password); authRequest.setDetails(authenticationDetailsSource.buildDetails(request)); Authentication authentication = authenticationManager.authenticate(authRequest); OAuth2AccessToken token = loginService.getAccessToken(authentication,Constants.USER_TYPE.admin.name()); stringCacheClient.set(TOKEN_KEY + SEPARATOR + user.getId(),token.getValue()); return returnData(R.SUCCESS.getCode(),token); }catch (BadCredentialsException e){ throw new ValidationException("用户名或密码错误"); }catch (UsernameNotFoundException e){ throw new ValidationException("用户不存在"); }catch (Exception e) { log.error(e.getMessage(), e); throw new BaseException(R.RUNTIME_EXCEPTION.getCode(),"登录错误"); } } @PostMapping("/login/admin/phone") @ApiOperation(value = "手机验证码登录系统", notes = "手机验证码登录系统") public ResponseEntity> loginPhone(HttpServletRequest request, @RequestBody UserLoginDTO loginDTO) { String tel = loginDTO.getUsername(); String smsCode = loginDTO.getSmsCode(); if (StringUtils.isBlank(tel)) { throw new ValidationException("手机号码不能为空"); } if (StringUtils.isBlank(smsCode)) { throw new ValidationException("手机验证码不能为空"); } //从缓存中获取验证码 String smsCacheCode = stringCacheClient.get(SMS_CODE_KEY + SEPARATOR + Constants.USER_TYPE.admin.name() + SEPARATOR + tel); if (!StringUtils.equals(smsCode, smsCacheCode)) { throw new ValidationException("手机验证码不正确"); } User user = userService.findByTel(tel, Constants.USER_TYPE.admin.name()); if(user == null){ throw new ValidationException("用户不存在"); } String tokenCache = stringCacheClient.get(TOKEN_KEY + SEPARATOR + user.getId()); if (StringUtils.isNotBlank(tokenCache)) { //强制删除token,下线 removeToken(tokenCache,user.getId()); } try { PhoneAuthenticationToken authRequest = new PhoneAuthenticationToken(tel, smsCode, Constants.USER_TYPE.admin.name()); authRequest.setDetails(authenticationDetailsSource.buildDetails(request)); Authentication authentication = authenticationManager.authenticate(authRequest); OAuth2AccessToken token = loginService.getAccessToken(authentication, Constants.USER_TYPE.admin.name()); //删除缓存中的验证码 stringCacheClient.delete(SMS_CODE_KEY + SEPARATOR + Constants.USER_TYPE.admin.name() + SEPARATOR + tel); stringCacheClient.set(TOKEN_KEY + SEPARATOR + user.getId(),token.getValue()); return returnData(R.SUCCESS.getCode(),token); }catch (UsernameNotFoundException e){ throw new ValidationException("用户不存在"); }catch (Exception e) { log.error(e.getMessage(), e); throw new BaseException(R.RUNTIME_EXCEPTION.getCode(),"登录错误"); } } @PostMapping("/logout") @ApiOperation(value = "登出系统", notes = "登出系统") public ResponseEntity logout( @RequestHeader(value = HttpHeaders.AUTHORIZATION, required = false) String token) { String userId = SecurityUtils.getUserId(); if (StringUtils.isNotBlank(token)) { stringCacheClient.delete(TOKEN_KEY + SEPARATOR + userId); String tokenValue = token.replace(OAuth2AccessToken.BEARER_TYPE, StrUtil.EMPTY).trim(); OAuth2AccessToken accessToken = tokenStore.readAccessToken(tokenValue); if (accessToken != null && StringUtils.isNotBlank(accessToken.getValue())) { tokenStore.removeAccessToken(accessToken); OAuth2RefreshToken refreshToken = accessToken.getRefreshToken(); tokenStore.removeRefreshToken(refreshToken); } } return returnData(R.SUCCESS.getCode(), null); } @PostMapping("/refresh-token") @ApiOperation(value = "刷新token", notes = "刷新token") public ResponseEntity refreshToken(HttpServletRequest request , @RequestHeader(value = "clientType", required = false) String clientType , @RequestBody RefreshTokenDto dto) { OAuth2AccessToken token = null; try { String refreshTokenValue = dto.getRefreshToken(); OAuth2RefreshToken refreshToken = this.tokenStore.readRefreshToken(refreshTokenValue); if (refreshToken == null) { throw new InvalidGrantException("Invalid refresh token: " + refreshTokenValue); } else { OAuth2Authentication authentication = this.tokenStore.readAuthenticationForRefreshToken(refreshToken); UserDTO p = (UserDTO)authentication.getPrincipal(); UserIdAuthenticationToken authRequest = new UserIdAuthenticationToken(p.getId(), p.getId()); authRequest.setDetails(authenticationDetailsSource.buildDetails(request)); Authentication a = authenticationManager.authenticate(authRequest); token = loginService.getAccessToken(a, clientType); } } catch (InvalidGrantException e) { throw new ValidationException("用户登录信息过期,请重新登录"); } return returnData(R.SUCCESS.getCode(), token); } @PostMapping("/token-check") @ApiOperation(value = "token验证是否过期", notes = "token验证是否过期") public ResponseEntity checkToken() { return returnData(R.SUCCESS.getCode(), null); } @GetMapping("/pub/captcha/{uuid}") public void captcha(HttpServletRequest request, HttpServletResponse response , @PathVariable("uuid") String uuid) throws Exception{ // 设置请求头为输出图片类型 response.setContentType("image/gif"); response.setHeader("Pragma", "No-cache"); response.setHeader("Cache-Control", "no-cache"); response.setDateHeader("Expires", 0); GifCaptcha gifCaptcha = new GifCaptcha(130,48,4); gifCaptcha.out(response.getOutputStream()); String verCode = gifCaptcha.text().toLowerCase(); log.info(verCode); stringCacheClient.set(captchaKey(uuid), verCode, 2*60); } private String captchaKey(String uuid){ return "captcha_" + uuid; } private void checkCaptchaCode(String uuid, String code){ if(StringUtils.isEmpty(code)){ throw new ValidationException("验证码不能为空"); } String key = captchaKey(uuid); String codeInCache = stringCacheClient.get(key); stringCacheClient.delete(key); if(StringUtils.isEmpty(codeInCache) || !codeInCache.equalsIgnoreCase(code)){ throw new ValidationException("无效验证码"); } } public void removeToken(String token,String userId) { if (StringUtils.isNotBlank(token) && StringUtils.isNotBlank(userId)) { stringCacheClient.delete(TOKEN_KEY + SEPARATOR + userId); String tokenValue = token.replace(OAuth2AccessToken.BEARER_TYPE, StrUtil.EMPTY).trim(); OAuth2AccessToken accessToken = tokenStore.readAccessToken(tokenValue); if (accessToken != null && StringUtils.isNotBlank(accessToken.getValue())) { tokenStore.removeAccessToken(accessToken); OAuth2RefreshToken refreshToken = accessToken.getRefreshToken(); tokenStore.removeRefreshToken(refreshToken); } } } }