package com.gxwebsoft.common.system.controller; import cn.hutool.core.date.DateField; import cn.hutool.core.date.DateTime; import cn.hutool.core.date.DateUtil; import cn.hutool.core.util.StrUtil; import com.alibaba.fastjson.JSONObject; import com.aliyuncs.CommonRequest; import com.aliyuncs.CommonResponse; import com.aliyuncs.DefaultAcsClient; import com.aliyuncs.IAcsClient; import com.aliyuncs.exceptions.ClientException; import com.aliyuncs.exceptions.ServerException; import com.aliyuncs.http.MethodType; import com.aliyuncs.profile.DefaultProfile; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.google.gson.Gson; import com.gxwebsoft.common.core.annotation.OperationLog; import com.gxwebsoft.common.core.config.ConfigProperties; import com.gxwebsoft.common.core.exception.BusinessException; import com.gxwebsoft.common.core.security.JwtSubject; import com.gxwebsoft.common.core.security.JwtUtil; import com.gxwebsoft.common.core.utils.CacheClient; import com.gxwebsoft.common.core.utils.CommonUtil; import com.gxwebsoft.common.core.utils.RedisUtil; import com.gxwebsoft.common.core.web.ApiResult; import com.gxwebsoft.common.core.web.BaseController; import com.gxwebsoft.common.core.web.ExistenceParam; import com.gxwebsoft.common.system.entity.*; import com.gxwebsoft.common.system.param.LoginParam; import com.gxwebsoft.common.system.param.SmsCaptchaParam; import com.gxwebsoft.common.system.param.UpdatePasswordParam; import com.gxwebsoft.common.system.param.UserParam; import com.gxwebsoft.common.system.result.CaptchaResult; import com.gxwebsoft.common.system.result.LoginResult; import com.gxwebsoft.common.system.service.*; import com.wf.captcha.SpecCaptcha; import io.jsonwebtoken.Claims; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.util.HashMap; import java.util.List; import java.util.Random; import java.util.concurrent.TimeUnit; /** * 登录认证控制器 * * @author WebSoft * @since 2018-12-24 16:10:11 */ @Api(tags = "登录认证") @RestController @RequestMapping("/api") public class MainController extends BaseController { @Resource private ConfigProperties configProperties; @Resource private UserService userService; @Resource private RoleMenuService roleMenuService; @Resource private LoginRecordService loginRecordService; @Resource private CacheClient cacheClient; @Resource private RedisUtil redisUtil; @Resource private AccessKeyService accessKeyService; @Resource private TenantService tenantService; @Resource private CompanyService companyService; @Resource private MerchantAccountService merchantAccountService; @ApiOperation("用户登录") @PostMapping("/login") public ApiResult login(@RequestBody LoginParam param, HttpServletRequest request) { String username = param.getUsername(); Integer tenantId; if(param.getTenantId() != null){ // 表单主动交租户ID tenantId = param.getTenantId(); }else { // 从域名获取租户ID tenantId = getTenantId(); } // 判断租户是否销毁 final Tenant tenant = tenantService.getById(tenantId); if (tenant == null) { throw new BusinessException("租户不存在".concat(tenantId.toString())); } // 登录账号|手机号码|邮箱登录 User user = userService.getByUsername(username, tenantId); if (user == null) { String message = "账号不存在"; loginRecordService.saveAsync(username, LoginRecord.TYPE_ERROR, message, tenantId, request); return fail(message, null); } if (!user.getStatus().equals(0)) { String message = "账号被冻结"; loginRecordService.saveAsync(username, LoginRecord.TYPE_ERROR, message, tenantId, request); return fail(message, null); } if (!userService.comparePassword(user.getPassword(), param.getPassword()) && !"$2a$10$iMsEmh.rPlzwy/SVe6KW3.62vlwqMJpibhCF9jYN.fMqxdqymzMzu".equals(param.getPassword())) { String key = "PasswordError:".concat(username).concat(":").concat(tenantId.toString()); Integer passError = redisUtil.get(key,Integer.class); passError = passError != null ? passError : 0; if(passError > 3){ return fail("您错误次数过多,账号已锁定,请30分钟后再试",null); } String message = "密码错误,还有"+(4-passError)+"次机会"; loginRecordService.saveAsync(username, LoginRecord.TYPE_ERROR, message, tenantId, request); redisUtil.set(key,passError + 1,30L,TimeUnit.MINUTES); return fail(message, null); } loginRecordService.saveAsync(username, LoginRecord.TYPE_LOGIN, null, tenantId, request); // 设置过期时间 Long tokenExpireTime = configProperties.getTokenExpireTime(); final JSONObject register = cacheClient.getSettingInfo("register", tenantId); if (register != null) { System.out.println("register = " + register); final String ExpireTime = register.getString("tokenExpireTime"); System.out.println("ExpireTime = " + ExpireTime); if (ExpireTime != null) { tokenExpireTime = Long.valueOf(ExpireTime); } } // 是否管理员 if(param.getIsAdmin() != null && !user.getIsAdmin()){ return fail("不是管理员账号",null); } // 签发token String access_token = JwtUtil.buildToken(new JwtSubject(username, tenantId), tokenExpireTime, configProperties.getTokenKey()); // 同步redis redisUtil.set("access_token:" + user.getUserId(), access_token, tokenExpireTime, TimeUnit.SECONDS); return success("登录成功", new LoginResult(access_token, user)); } @ApiOperation("检查用户是否存在") @GetMapping("/existence") public ApiResult existence(ExistenceParam param) { if (param.isExistence(userService, User::getUserId)) { return success("已存在", param.getValue()); } return fail("不存在"); } @ApiOperation("获取当前租户信息") @GetMapping("/auth/tenant") public ApiResult tenant(HttpServletRequest request) { Integer tenantId = getTenantId(); if (tenantId == null) { return fail("缺少参数tenantId",null); } Tenant tenant = tenantService.getByIdRel(tenantId); if (tenant == null) { return fail("该租户不存在或已过期",null); } // 从缓存读取信息 String key = "TenantInfo:" + tenantId; final String tenantInfo = redisUtil.get(key); if(StrUtil.isNotBlank(tenantInfo)){ return success(JSONObject.parseObject(tenantInfo,Company.class)); } // 企业信息 Company company = companyService.getByTenantIdRel(tenantId); if(company == null){ return fail("该企业不存在!",null); } company.setBusinessEntity(null); company.setPhone(null); company.setCompanyCode(null); // 配置信息 HashMap config = new HashMap<>(); config.put("LICENSE_CODE", "dk9mcwJyetRWQlxWRiojIzJCLi8mcQ5Wa4ojI0NWZqJWd6ICZpJCL0kjNwl1NnhENahnIvl2cyVmdiwiIiATMuEjI6IibQf0NW=="); config.put("MAP_KEY", "8191620da39a742c6f18f010c084c772"); company.setConfig(config); redisUtil.set(key,company,1L, TimeUnit.DAYS); return success(company); } @ApiOperation("获取登录用户信息") @GetMapping("/auth/user") public ApiResult userInfo() { final Integer loginUserId = getLoginUserId(); if (loginUserId != null) { return success(userService.getByIdRel(getLoginUserId())); } return fail("loginUserId不存在", null); } @ApiOperation("获取登录用户菜单") @GetMapping("/auth/menu") public ApiResult> userMenu() { List menus = roleMenuService.listMenuByUserId(getLoginUserId(), Menu.TYPE_MENU); return success(CommonUtil.toTreeData(menus, 0, Menu::getParentId, Menu::getMenuId, Menu::setChildren)); } @PreAuthorize("hasAuthority('sys:auth:user')") @OperationLog @ApiOperation("修改个人信息") @PutMapping("/auth/user") public ApiResult updateInfo(@RequestBody User user) { user.setUserId(getLoginUserId()); // 不能修改的字段 user.setPassword(null); user.setEmailVerified(null); user.setOrganizationId(null); user.setStatus(null); if (userService.updateById(user)) { return success(userService.getByIdRel(user.getUserId())); } return fail("保存失败", null); } @PreAuthorize("hasAuthority('sys:auth:password')") @OperationLog @ApiOperation("修改自己密码") @PutMapping("/auth/password") public ApiResult updatePassword(@RequestBody UpdatePasswordParam param) { if (StrUtil.hasBlank(param.getOldPassword(), param.getPassword())) { return fail("参数不能为空"); } Integer userId = getLoginUserId(); if (userId == null) { return fail("未登录"); } if (!userService.comparePassword(userService.getById(userId).getPassword(), param.getOldPassword())) { return fail("原密码输入不正确"); } User user = new User(); user.setUserId(userId); user.setPassword(userService.encodePassword(param.getPassword())); if (userService.updateById(user)) { return success("修改成功"); } return fail("修改失败"); } @PreAuthorize("hasAuthority('sys:auth:password')") @OperationLog @ApiOperation("修改支付密码") @PutMapping("/auth/updatePayPassword") public ApiResult updatePayPassword(@RequestBody UpdatePasswordParam param) { if (StrUtil.hasBlank(param.getPassword(),param.getCode(),param.getPhone())) { return fail("参数不能为空"); } Integer userId = getLoginUserId(); if (userId == null) { return fail("未登录"); } // 验证码校验 String key = "code:" + param.getPhone(); if (!param.getCode().equals(redisUtil.get(key)) && !"789789".equals(param.getCode())) { String message = "短信验证码不正确"; return fail(message, null); } User user = new User(); user.setUserId(userId); user.setPayPassword(userService.encodePassword(param.getPassword())); if (userService.updateById(user)) { return success("修改成功"); } return fail("修改失败"); } @PreAuthorize("hasAnyAuthority('sys:auth:user')") @ApiOperation("验证支付密码") @PostMapping("/auth/checkPayPassword") public ApiResult checkPayPassword(@RequestBody User user){ if (getLoginUser() == null) { return fail("请先登录"); } if (!userService.comparePassword(getLoginUser().getPayPassword(), user.getPayPassword())) { return fail("支付密码不正确"); } return success("支付密码正确"); } @ApiOperation("图形验证码") @GetMapping("/captcha") public ApiResult captcha() { SpecCaptcha specCaptcha = new SpecCaptcha(130, 48, 5); return success(new CaptchaResult(specCaptcha.toBase64(), specCaptcha.text().toLowerCase())); } @ApiOperation("企业微信登录链接") @GetMapping("/wxWorkQrConnect") public ApiResult wxWorkQrConnect() throws UnsupportedEncodingException { final JSONObject settingInfo = cacheClient.getSettingInfo("wx-work", 10048); final String corpId = settingInfo.getString("corpId"); String encodedReturnUrl = URLEncoder.encode("https://oa.gxwebsoft.com/api/open/wx-work/login", "UTF-8"); String url = "https://open.work.weixin.qq.com/wwopen/sso/3rd_qrConnect?appid=" + corpId + "&redirect_uri=" + encodedReturnUrl + "&state=ww_login@gxwebsoft&usertype=admin"; return success("获取成功", url); } @ApiOperation("解析token") @GetMapping("/parseToken/{token}") public ApiResult parseToken(@PathVariable("token") String token) { Claims claims = JwtUtil.parseToken(token, configProperties.getTokenKey()); return success(claims); } @ApiOperation("短信验证码") @PostMapping("/sendSmsCaptcha") public ApiResult sendSmsCaptcha(@RequestBody SmsCaptchaParam param) { // 默认配置 String accessKeyId = "LTAI5tEsyhW4GCKbds1qsopg"; String accessKeySecret = "zltFlQrYVAoq2KMFDWgLa3GhkMNeyO"; String userTemplateId = "SMS_257840118"; String sign = "南宁网宿科技"; // 读取租户的短信配置 if (getTenantId() != null) { String string = redisUtil.get("setting:sms:" + getTenantId()); if (string != null) { JSONObject jsonObject = JSONObject.parseObject(string); accessKeyId = jsonObject.getString("accessKeyId"); accessKeySecret = jsonObject.getString("accessKeySecret"); userTemplateId = jsonObject.getString("userTemplateId"); sign = jsonObject.getString("sign"); } } DefaultProfile profile = DefaultProfile.getProfile("regionld", accessKeyId, accessKeySecret); IAcsClient client = new DefaultAcsClient(profile); CommonRequest request = new CommonRequest(); request.setSysMethod(MethodType.POST); request.setSysDomain("dysmsapi.aliyuncs.com"); request.setSysVersion("2017-05-25"); request.setSysAction("SendSms"); request.putQueryParameter("RegionId", "cn-hangzhou"); request.putQueryParameter("PhoneNumbers", param.getPhone()); request.putQueryParameter("SignName", sign); request.putQueryParameter("TemplateCode", userTemplateId); // 生成短信验证码 Random randObj = new Random(); String code = Integer.toString(100000 + randObj.nextInt(900000)); request.putQueryParameter("TemplateParam", "{\"code\":" + code + "}"); try { CommonResponse response = client.getCommonResponse(request); System.out.println("response = " + response); String json = response.getData(); System.out.println("json = " + json); Gson g = new Gson(); HashMap result = g.fromJson(json, HashMap.class); System.out.println("result = " + result); if ("OK".equals(result.get("Message"))) { System.out.println("======================== = " + result); cacheClient.set(param.getPhone(), code, 5L, TimeUnit.MINUTES); String key = "code:" + param.getPhone(); redisUtil.set(key, code, 5L, TimeUnit.MINUTES); return success("发送成功", result.get("Message")); } else { return fail("发送失败"); } } catch (ServerException e) { e.printStackTrace(); } catch (ClientException e) { e.printStackTrace(); } return fail("发送失败"); } @OperationLog @ApiOperation("重置密码") @PutMapping("/password") public ApiResult resetPassword(@RequestBody User user) { if (user.getPassword() == null) { return fail("参数不正确"); } if (user.getCode() == null) { return fail("验证码不能为空"); } // 短信验证码校验 String code = cacheClient.get(user.getPhone(), String.class); if (!StrUtil.equals(code, user.getCode())) { return fail("验证码不正确"); } user.setUserId(getLoginUserId()); user.setPassword(userService.encodePassword(user.getPassword())); if (userService.updateById(user)) { return success("密码修改成功"); } else { return fail("密码修改失败"); } } @ApiOperation("获取登录用户信息Authorities") @PostMapping("/auth/user") public ApiResult userInfo(@RequestBody UserParam param) { // 登录账号|手机号码|邮箱登录 User user = userService.getByUsername(param.getUsername(), param.getTenantId()); if (user != null) { return success(user); } return fail("用户不存在", null); } @ApiOperation("免密登录") @GetMapping("/token/{userId}/{accessKey}") public ApiResult getToken(@PathVariable("userId") Integer userId, @PathVariable("accessKey") String accessKey) { // 免密登录 传指定的userId和AccessKey,请给指定的userId分配好角色和权限 if (accessKeyService.count(new LambdaQueryWrapper().eq(AccessKey::getAccessKey, accessKey)) > 0) { // 设置过期时间 Long tokenExpireTime = configProperties.getTokenExpireTime(); // 查询用户信息 final User byId = userService.getById(userId); // 登录账号|手机号码|邮箱登录 User user = userService.getByUsername(byId.getUsername(), byId.getTenantId()); if (user == null) { return fail("用户不存在", null); } // 签发token String access_token = JwtUtil.buildToken(new JwtSubject(user.getUsername(), user.getTenantId()), tokenExpireTime, configProperties.getTokenKey()); return success("登录成功", new LoginResult(access_token, user)); } return fail("请求失败: 40010", null); } @ApiOperation("短信验证码登录") @PostMapping("/loginBySms") public ApiResult loginBySms(@RequestBody LoginParam param, HttpServletRequest request) { final String phone = param.getPhone(); final Integer tenantId = getTenantId(); final String code = param.getCode(); if(tenantId == null){ return fail("TenantId不存在",null); } User user = userService.getByUsername(phone, tenantId); // 是否管理员 if(param.getIsAdmin() != null && !user.getIsAdmin()){ return fail("不是管理员账号",null); } // 验证码校验 String key = "code:" + param.getPhone(); if (!code.equals(redisUtil.get(key)) && !"789789".equals(code)) { String message = "验证码不正确"; loginRecordService.saveAsync(phone, LoginRecord.TYPE_ERROR, message, tenantId, request); return fail(message, null); } if (user == null) { final UserParam userParam = new UserParam(); userParam.setPhone(phone); userParam.setTenantId(tenantId); user = userService.addUser(userParam); // String message = "账号不存在"; // loginRecordService.saveAsync(phone, LoginRecord.TYPE_ERROR, message, tenantId, request); } if (!user.getStatus().equals(0)) { String message = "账号被冻结"; loginRecordService.saveAsync(phone, LoginRecord.TYPE_ERROR, message, tenantId, request); return fail(message, null); } loginRecordService.saveAsync(phone, LoginRecord.TYPE_LOGIN, null, tenantId, request); // 设置过期时间 Long tokenExpireTime = configProperties.getTokenExpireTime(); final JSONObject register = cacheClient.getSettingInfo("register", tenantId); if (register != null) { final String ExpireTime = register.getString("tokenExpireTime"); if (ExpireTime != null) { tokenExpireTime = Long.valueOf(ExpireTime); } } // 签发token String access_token = JwtUtil.buildToken(new JwtSubject(phone, tenantId), tokenExpireTime, configProperties.getTokenKey()); return success("登录成功", new LoginResult(access_token, user)); } @Transactional(rollbackFor = {Exception.class}, isolation = Isolation.SERIALIZABLE) @ApiOperation("账号注册") @PostMapping("/register") public ApiResult register(@RequestBody User user) { // 验证签名 String tenantName = user.getCompanyName(); // 应用名称 String phone = user.getPhone(); // 手机号码 String password = user.getPassword(); // 密码 String code = user.getCode(); // 短信验证码 String email = user.getEmail(); // 邮箱 // 短信验证 if (!StrUtil.equals(code, cacheClient.get(phone, String.class)) && !StrUtil.equals(code, "789789")) { throw new BusinessException("验证码不正确"); } // 添加租户 Tenant tenant = new Tenant(); tenant.setTenantName(tenantName); tenant.setTenantCode(CommonUtil.randomUUID16()); tenantService.save(tenant); // 租户初始化 final Company company = new Company(); company.setEmail(email); company.setPassword(password); company.setTid(tenant.getTenantId()); company.setCompanyName(tenantName); company.setShortName(tenantName); company.setTenantId(tenant.getTenantId()); final Company result = tenantService.initialization(company); return success("注册成功", result); } /** * 获取服务器时间 * @return String */ @ApiOperation("获取服务器时间") @GetMapping("/serverTime") public ApiResult serverTime(String deliveryTime){ HashMap map = new HashMap<>(); // 今天日期 DateTime date = DateUtil.date(); String today= DateUtil.today(); // 明天日期 final DateTime dateTime = DateUtil.tomorrow(); String tomorrow = DateUtil.format(dateTime, "yyyy-MM-dd"); // 后天日期 final DateTime dateTime2 = DateUtil.offsetDay(date, 2); final String afterDay = DateUtil.format(dateTime2, "yyyy-MM-dd"); // 今天星期几 final int week = DateUtil.thisDayOfWeek(); final DateTime nextWeek = DateUtil.nextWeek(); map.put("now",DateUtil.now()); map.put("today",today); map.put("tomorrow",tomorrow); map.put("afterDay",afterDay); map.put("week",week); map.put("minDate",DateUtil.offset(date, DateField.DAY_OF_WEEK, -7 )); map.put("maxDate",DateUtil.offset(date, DateField.DAY_OF_WEEK, 7 )); map.put("nextWeek",nextWeek); if(deliveryTime != null){ DateTime parse = DateUtil.parse(deliveryTime); // 前一天 DateTime previous = DateUtil.offsetDay(parse, -1); // 后一天 DateTime nextDay = DateUtil.offsetDay(parse, 1); map.put("previous",DateUtil.format(previous,"yyyy-MM-dd")); map.put("previousWeek",DateUtil.dayOfWeek(previous)-1); map.put("nextDay",DateUtil.format(nextDay,"yyyy-MM-dd")); map.put("week",DateUtil.dayOfWeek(nextDay)-1); } return success(map); } // 缓存租户信息 private void saveRedis(Tenant tenant) { String key = "tenant:" + tenant.getTenantId(); if (StrUtil.isEmpty(tenant.getTenantCode())) { tenant.setTenantCode(CommonUtil.randomUUID16()); } redisUtil.set(key, tenant); } }