package com.oying.modules.security.rest; import cn.hutool.core.util.IdUtil; import com.oying.modules.security.config.CaptchaConfig; import com.oying.modules.security.config.LoginProperties; import com.oying.modules.security.config.SecurityProperties; import com.oying.modules.security.config.enums.LoginCodeEnum; import com.oying.modules.security.security.TokenProvider; import com.oying.modules.security.service.OnlineUserService; import com.oying.modules.security.service.UserDetailsServiceImpl; import com.oying.modules.security.service.dto.AuthUserDto; import com.oying.modules.security.service.dto.JwtUserDto; import com.wf.captcha.base.Captcha; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import com.oying.annotation.Log; import com.oying.annotation.rest.AnonymousDeleteMapping; import com.oying.annotation.rest.AnonymousGetMapping; import com.oying.annotation.rest.AnonymousPostMapping; import com.oying.config.properties.RsaProperties; import com.oying.exception.BadRequestException; import com.oying.utils.RsaUtils; import com.oying.utils.RedisUtils; import com.oying.utils.SecurityUtils; import com.oying.utils.StringUtils; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletRequest; import java.util.HashMap; import java.util.Map; import java.util.concurrent.TimeUnit; /** * @author Z * @date 2018-11-23 * 授权、根据token获取用户详细信息 */ @Slf4j @RestController @RequestMapping("/auth") @RequiredArgsConstructor @Api(tags = "系统:系统授权接口") public class AuthController { private final SecurityProperties properties; private final RedisUtils redisUtils; private final OnlineUserService onlineUserService; private final TokenProvider tokenProvider; private final CaptchaConfig captchaConfig; private final LoginProperties loginProperties; private final PasswordEncoder passwordEncoder; private final UserDetailsServiceImpl userDetailsService; @Log("用户登录") @ApiOperation("登录授权") @AnonymousPostMapping(value = "/login") public ResponseEntity login(@Validated @RequestBody AuthUserDto authUser, HttpServletRequest request) throws Exception { // 密码解密 String password = RsaUtils.decryptByPrivateKey(RsaProperties.privateKey, authUser.getPassword()); // 查询验证码 String code = redisUtils.get(authUser.getUuid(), String.class); // 清除验证码 redisUtils.del(authUser.getUuid()); if (StringUtils.isBlank(code)) { throw new BadRequestException("验证码不存在或已过期"); } if (StringUtils.isBlank(authUser.getCode()) || !authUser.getCode().equalsIgnoreCase(code)) { throw new BadRequestException("验证码错误"); } // 获取用户信息 JwtUserDto jwtUser = userDetailsService.loadUserByUsername(authUser.getUsername()); // 验证用户密码 if (!passwordEncoder.matches(password, jwtUser.getPassword())) { throw new BadRequestException("登录密码错误"); } Authentication authentication = new UsernamePasswordAuthenticationToken(jwtUser, null, jwtUser.getAuthorities()); SecurityContextHolder.getContext().setAuthentication(authentication); // 生成令牌 String token = tokenProvider.createToken(jwtUser); // 返回 token 与 用户信息 Map authInfo = new HashMap(2) {{ put("token", properties.getTokenStartWith() + token); put("user", jwtUser); }}; if (loginProperties.isSingleLogin()) { // 踢掉之前已经登录的token onlineUserService.kickOutForUsername(authUser.getUsername()); } // 保存在线信息 onlineUserService.save(jwtUser, token, request); // 返回登录信息 return ResponseEntity.ok(authInfo); } @ApiOperation("临时授权") @AnonymousGetMapping(value = "/token") public ResponseEntity loginTest(@RequestParam String username, HttpServletRequest request) { // 生成令牌与第三方系统获取令牌方式 JwtUserDto jwtUser = userDetailsService.loadUserByUsername(username); Authentication authentication = new UsernamePasswordAuthenticationToken(jwtUser, null, jwtUser.getAuthorities()); SecurityContextHolder.getContext().setAuthentication(authentication); // 生成令牌 String token = tokenProvider.createToken(jwtUser); // 返回 token 与 用户信息 Map authInfo = new HashMap(2) {{ put("token", properties.getTokenStartWith() + token); put("user", jwtUser); }}; // 保存在线信息 onlineUserService.save(jwtUser, token, request); // 返回登录信息 return ResponseEntity.ok(authInfo); } @ApiOperation("获取用户信息") @GetMapping(value = "/info") public ResponseEntity getUserInfo() { JwtUserDto jwtUser = (JwtUserDto) SecurityUtils.getCurrentUser(); return ResponseEntity.ok(jwtUser); } @ApiOperation("获取验证码") @AnonymousGetMapping(value = "/code") public ResponseEntity getCode() { // 获取运算的结果 Captcha captcha = captchaConfig.getCaptcha(); String uuid = properties.getCodeKey() + IdUtil.simpleUUID(); //当验证码类型为 arithmetic时且长度 >= 2 时,captcha.text()的结果有几率为浮点型 String captchaValue = captcha.text(); if (captcha.getCharType() - 1 == LoginCodeEnum.ARITHMETIC.ordinal() && captchaValue.contains(".")) { captchaValue = captchaValue.split("\\.")[0]; } // 保存 redisUtils.set(uuid, captchaValue, captchaConfig.getExpiration(), TimeUnit.MINUTES); // 验证码信息 Map imgResult = new HashMap(2) {{ put("img", captcha.toBase64()); put("uuid", uuid); }}; return ResponseEntity.ok(imgResult); } @ApiOperation("退出登录") @AnonymousDeleteMapping(value = "/logout") public ResponseEntity logout(HttpServletRequest request) { String token = tokenProvider.getToken(request); onlineUserService.logout(token); return new ResponseEntity<>(HttpStatus.OK); } }