package com.oying.modules.security.rest; import cn.hutool.core.util.IdUtil; import com.alibaba.fastjson2.JSONObject; 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.WeiXinService; import com.oying.modules.security.service.dto.AuthUserDto; import com.oying.modules.security.service.dto.AuthUserPhoneDto; import com.oying.modules.security.service.dto.AuthUserWeixinDto; import com.oying.modules.security.service.dto.JwtUserDto; import com.oying.modules.system.domain.DictDetail; import com.oying.modules.system.domain.Role; import com.oying.modules.system.domain.User; import com.oying.modules.system.service.DictDetailService; import com.oying.modules.system.service.UserService; import com.oying.utils.*; 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 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.crypto.password.PasswordEncoder; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletRequest; import java.util.*; 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; private final WeiXinService weiXinService; private final UserService userService; private final DictDetailService dictDetailService; @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 (!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(R.success(authInfo)); } @Log("短信验证码登录") @ApiOperation("短信验证码登录") @AnonymousPostMapping(value = "/login/phone") public ResponseEntity loginPhone(@Validated @RequestBody AuthUserPhoneDto authUser, HttpServletRequest request) { // 查询验证码 String code = redisUtils.get(authUser.getUuid(), String.class); // 清除验证码 redisUtils.del(authUser.getUuid()); if (StringUtils.isBlank(code)) { throw new BadRequestException("验证码不存在或已过期"); } if (!authUser.getCode().equalsIgnoreCase(code)) { throw new BadRequestException("验证码错误"); } // 获取用户信息 JwtUserDto jwtUser = userDetailsService.loadUserByUsername(authUser.getUsername()); 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(R.success(authInfo)); } public static final String OLD = "OLD"; public static final String NEW = "NEW"; @Log("小程序:微信授权登录") @ApiOperation("小程序:微信授权登录") @AnonymousPostMapping(value = "/login/weixin") public ResponseEntity loginWeixin(@Validated @RequestBody AuthUserWeixinDto authUser, HttpServletRequest request) throws Exception { JSONObject jsonObject; switch (authUser.getType()) { case OLD: jsonObject = weiXinService.code2Session(authUser.getCode()); String openid = jsonObject.getString("openid"); User userDto = userService.findByOpenid(openid); if (userDto == null) { return ResponseEntity.ok(R.success(openid)); } authUser.setUsername(userDto.getUsername()); break; case NEW: jsonObject = weiXinService.getPhoneNumber(authUser.getCode()); String phone = jsonObject.getJSONObject("phone_info").getString("purePhoneNumber"); User user1 = userService.findByName(phone); if (user1 == null) { //创建用户 User user = new User(); user.setUsername(phone); user.setNickName("OYING-" + phone); user.setUserType(ConstantsKey.BUYER); user.setEnabled(true); getRole(user); user.setPassword(passwordEncoder.encode(phone.substring(phone.length() - 6))); user.setOpenid(authUser.getUsername()); userService.create(user); } else { if (user1.getUserType().equals(ConstantsKey.BUYER)) { if (user1.getRoles() == null) { getRole(user1); } } user1.setOpenid(authUser.getUsername()); userService.update(user1); } authUser.setUsername(phone); break; default: throw new BadRequestException("登录类型错误"); } // 获取用户信息 JwtUserDto jwtUser = userDetailsService.loadUserByUsername(authUser.getUsername()); 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(R.success(authInfo)); } public void getRole(User user1) { Set roles = new HashSet<>(); Role role = new Role(); DictDetail detail = dictDetailService.getDictByName(ConstantsKey.USER_TYPE_BUYER).get(0); role.setId(Long.valueOf(detail.getLabel())); roles.add(role); user1.setRoles(roles); } @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(R.success(authInfo)); } @ApiOperation("获取用户信息") @GetMapping(value = "/info") public ResponseEntity getUserInfo() { JwtUserDto jwtUser = (JwtUserDto) SecurityUtils.getCurrentUser(); return ResponseEntity.ok(R.success(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(R.success(imgResult)); } @ApiOperation("退出登录") @AnonymousDeleteMapping(value = "/logout") public ResponseEntity logout(HttpServletRequest request) { String token = tokenProvider.getToken(request); onlineUserService.logout(token); return new ResponseEntity<>(R.success(), HttpStatus.OK); } }