业务代码/** * 微信登录 */ @Override @Transactional(rollbackFor = Exception.class) public AjaxResult wxLogin(WeChatCodeRequest request, HttpServletRequest httpServletRequest) { String code = request.getCode(); //通过code获取手机号 JSONObject jsonObject = WechatUtil.getPhoneNumber(code); JSONObject js = (JSONObject) jsonObject.get("phone_info"); String phoneNumber = (String) js.get("phoneNumber"); //对手机号进行判断,登录 //剩下的是校验成功的情况 if (iSysUserService.getLoginUserByPhone(phoneNumber) == null) { return AjaxResult.error("登录失败,用户不存在或不可用,请联系管理员注册"); } else { AjaxResult ajax = new AjaxResult(); //说明在User表中已经存在,则将二者绑定到一起,使用user信息进行登录 redisCache.deleteObject(CacheConstants.CAPTCHA_CODE_KEY + phoneNumber); // LoginUser loginUser = tokenService.getLoginUser(httpServletRequest); LoginUser loginUser = iSysUserService.getLoginUserByPhone(phoneNumber); List<SysUserRole> sysUserRoles = sysUserRoleMapper.selectList(new LambdaQueryWrapper<SysUserRole>().eq(SysUserRole::getUserId, loginUser.getUserId())); List<SysRole> collect = sysUserRoles.stream().map(i -> { //拿着roleid查询role SysRole sysRole = roleMapper.selectRoleById(i.getRoleId()); return sysRole; }).collect(Collectors.toList()); LambdaUpdateWrapper<SysUser> uw = new LambdaUpdateWrapper<SysUser>() .set(SysUser::getLoginIp, ServletUtil.getClientIP(httpServletRequest)) .set(SysUser::getLoginDate, new Date()) .set(SysUser::getUpdateTime, new Date()) .eq(SysUser::getPhonenumber, phoneNumber) .eq(SysUser::getStatus, "0") .eq(SysUser::getDelFlag, "0"); iSysUserService.update(uw); // 生成token //设置角色 loginUser.getUser().setRoles(collect); //设置权限 // loginUser.setPermissions(); String token = tokenService.createToken(loginUser); ajax.put(Constants.TOKEN, token); tokenService.setLoginUser(loginUser); return ajax; } }
tokenService工具类package mmm.crm.system.web.service; import java.util.HashMap; import java.util.Map; import java.util.concurrent.TimeUnit; import javax.servlet.http.HttpServletRequest; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import mmm.crm.common.constant.CacheConstants; import mmm.crm.common.constant.Constants; import mmm.crm.common.core.domain.model.LoginUser; import mmm.crm.common.core.redis.RedisCache; import mmm.crm.common.utils.ServletUtils; import mmm.crm.common.utils.StringUtils; import mmm.crm.common.utils.ip.AddressUtils; import mmm.crm.common.utils.ip.IpUtils; import mmm.crm.common.utils.uuid.IdUtils; import eu.bitwalker.useragentutils.UserAgent; import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; /** * token验证处理 */ @Component public class TokenService { // 令牌自定义标识 @Value("${token.header}") private String header; // 令牌秘钥 @Value("${token.secret}") private String secret; // 令牌有效期(默认30分钟) @Value("${token.expireTime}") private int expireTime; protected static final long MILLIS_SECOND = 1000; protected static final long MILLIS_MINUTE = 60 * MILLIS_SECOND; private static final Long MILLIS_MINUTE_TEN = 20 * 60 * 1000L; @Autowired private RedisCache redisCache; /** * 获取用户身份信息 * * @return 用户信息 */ public LoginUser getLoginUser(HttpServletRequest request) { // 获取请求携带的令牌 String token = getToken(request); if (StringUtils.isNotEmpty(token)) { try { Claims claims = parseToken(token); // 解析对应的权限以及用户信息 String uuid = (String) claims.get(Constants.LOGIN_USER_KEY); String userKey = getTokenKey(uuid); LoginUser user = redisCache.getCacheObject(userKey); return user; } catch (Exception e) { } } return null; } /** * 设置用户身份信息 */ public void setLoginUser(LoginUser loginUser) { if (StringUtils.isNotNull(loginUser) && StringUtils.isNotEmpty(loginUser.getToken())) { refreshToken(loginUser); } } /** * 删除用户身份信息 */ public void delLoginUser(String token) { if (StringUtils.isNotEmpty(token)) { String userKey = getTokenKey(token); redisCache.deleteObject(userKey); } } /** * 创建令牌 * * @param loginUser 用户信息 * @return 令牌 */ public String createToken(LoginUser loginUser) { String token = IdUtils.fastUUID(); loginUser.setToken(token); setUserAgent(loginUser); refreshToken(loginUser); Map<String, Object> claims = new HashMap<>(); claims.put(Constants.LOGIN_USER_KEY, token); return createToken(claims); } /** * 验证令牌有效期,相差不足20分钟,自动刷新缓存 * * @param loginUser * @return 令牌 */ public void verifyToken(LoginUser loginUser) { long expireTime = loginUser.getExpireTime(); long currentTime = System.currentTimeMillis(); if (expireTime - currentTime <= MILLIS_MINUTE_TEN) { refreshToken(loginUser); } } /** * 刷新令牌有效期 * * @param loginUser 登录信息 */ public void refreshToken(LoginUser loginUser) { loginUser.setLoginTime(System.currentTimeMillis()); loginUser.setExpireTime(loginUser.getLoginTime() + expireTime * MILLIS_MINUTE); // 根据uuid将loginUser缓存 String userKey = getTokenKey(loginUser.getToken()); redisCache.setCacheObject(userKey, loginUser, expireTime, TimeUnit.MINUTES); } /** * 设置用户代理信息 * * @param loginUser 登录信息 */ public void setUserAgent(LoginUser loginUser) { UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent")); String ip = IpUtils.getIpAddr(); loginUser.setIpaddr(ip); loginUser.setLoginLocation(AddressUtils.getRealAddressByIP(ip)); loginUser.setBrowser(userAgent.getBrowser().getName()); loginUser.setOs(userAgent.getOperatingSystem().getName()); } /** * 从数据声明生成令牌 * * @param claims 数据声明 * @return 令牌 */ private String createToken(Map<String, Object> claims) { String token = Jwts.builder() .setClaims(claims) .signWith(SignatureAlgorithm.HS512, secret).compact(); return token; } /** * 从令牌中获取数据声明 * * @param token 令牌 * @return 数据声明 */ private Claims parseToken(String token) { return Jwts.parser() .setSigningKey(secret) .parseClaimsJws(token) .getBody(); } /** * 从令牌中获取用户名 * * @param token 令牌 * @return 用户名 */ public String getUsernameFromToken(String token) { Claims claims = parseToken(token); return claims.getSubject(); } /** * 获取请求token * * @param request * @return token */ public String getToken(HttpServletRequest request) { String token = request.getHeader(header); if (StringUtils.isNotEmpty(token) && token.startsWith(Constants.TOKEN_PREFIX)) { token = token.replace(Constants.TOKEN_PREFIX, ""); } return token; } private String getTokenKey(String uuid) { return CacheConstants.LOGIN_TOKEN_KEY + uuid; } }
微信登录工具类
package mmm.crm.common.utils; import cn.hutool.http.Header; import cn.hutool.http.HttpRequest; import cn.hutool.http.HttpResponse; import cn.hutool.http.HttpUtil; import cn.hutool.json.JSONUtil; import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSONObject; import com.google.common.net.HttpHeaders; import com.google.gson.Gson; import com.google.gson.JsonObject; import lombok.extern.log4j.Log4j; import mmm.crm.common.utils.http.HttpUtils; import mmm.crm.common.utils.sign.Base64; import org.apache.http.HttpEntity; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.util.EntityUtils; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.client.RestTemplate; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import javax.servlet.FilterConfig; import javax.xml.bind.ValidationException; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.security.*; import java.security.spec.InvalidParameterSpecException; import java.util.Arrays; import java.util.HashMap; import java.util.Map; /** * @Author:XGY * @Date:2023/4/24 18:14 */ public class WechatUtil { public static JSONObject getSessionKeyOrOpenId(String code) throws IOException { //微信端登录code String wxCode = code; String requestUrl = "https://api.weixin.qq.com/sns/jscode2session"; Map<String, String> requestUrlParam = new HashMap<>(); //小程序appId requestUrlParam.put("appid", "wx88c4501ad86e1174"); //小程序secret requestUrlParam.put("secret", "c08515ced6cd60e976028be5d0d43498"); //小程序端返回的code requestUrlParam.put("js_code", wxCode); //默认参数 requestUrlParam.put("grant_type", "authorization_code"); //发送post请求读取调用微信接口获取openid用户唯一标识 JSONObject jsonObject = JSON.parseObject(HttpClientUtil.doPost(requestUrl, requestUrlParam)); return jsonObject; } public static com.alibaba.fastjson2.JSONObject getUserInfo(String encryptedData, String sessionKey, String iv) { // 被加密的数据 byte[] dataByte = Base64.decode(encryptedData); // 加密秘钥 byte[] keyByte = Base64.decode(sessionKey); // 偏移量 byte[] ivByte = Base64.decode(iv); try { // 如果密钥不足16位,那么就补足. 这个if 中的内容很重要 int base = 16; if (keyByte.length % base != 0) { int groups = keyByte.length / base + (keyByte.length % base != 0 ? 1 : 0); byte[] temp = new byte[groups * base]; Arrays.fill(temp, (byte) 0); System.arraycopy(keyByte, 0, temp, 0, keyByte.length); keyByte = temp; } // 初始化 Security.addProvider(new BouncyCastleProvider()); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding", "BC"); SecretKeySpec spec = new SecretKeySpec(keyByte, "AES"); AlgorithmParameters parameters = AlgorithmParameters.getInstance("AES"); parameters.init(new IvParameterSpec(ivByte)); cipher.init(Cipher.DECRYPT_MODE, spec, parameters);// 初始化 byte[] resultByte = cipher.doFinal(dataByte); if (null != resultByte && resultByte.length > 0) { String result = new String(resultByte, "UTF-8"); return JSON.parseObject(result); } } catch (Exception e) { new Exception(e); } return null; } // public static String getPhone(String code) throws ValidationException, IOException { // final String appID = "wx88c4501ad86e1174"; // final String appSecret = "c08515ced6cd60e976028be5d0d43498"; // //使用appid和密钥获取access_token String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + appID + "&secret=" + appSecret; String s = HttpUtils.sendGet(url); JSONObject jsonObject = HttpUtils.doGetJson(url); // //使用access_token和code获取手机号 // JSONObject accessToken = getAccessToken(appID, appSecret); // String url1 = "https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=" + accessToken.getString("access_token"); // JSONObject data = new JSONObject(); // data.put("code", code); // String phoneNumber = null; // try { // JSONObject jsonObject1 = HttpUtils.doPost(url1, code); // JSONObject phone_info = (JSONObject) jsonObject1.get("phone_info"); // phoneNumber = phone_info.getString("phoneNumber"); // } catch (Exception e) { // throw new RuntimeException(e); // } // return phoneNumber; // } // public static JSONObject getAccessToken(String appid, String secretKey) { // String result = null; // try { // String baseUrl = "https://api.weixin.qq.com/cgi-bin/token"; // HashMap<String, Object> requestParam = new HashMap<>(); // // 小程序 appId // requestParam.put("grant_type", "client_credential"); // // 小程序唯一凭证id // requestParam.put("appid", appid); // // 小程序 appSecret(小程序的唯一凭证密钥) // requestParam.put("secret", secretKey); // // 发送GET请求读取调用微信接口获取openid用户唯一标识 // result = HttpUtil.get(baseUrl, requestParam); // } catch (Exception e) { // e.printStackTrace(); // } // return JSONObject.parseObject(result); // } public static String getAccessTokenOne(String appId,String appSecret) { String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + appId + "&secret=" + appSecret; System.out.println("request user info from url: {" + url + "}"); JsonObject accessTokenInfo = null; try { DefaultHttpClient httpClient = new DefaultHttpClient(); HttpGet httpGet = new HttpGet(url); CloseableHttpResponse httpResponse = httpClient.execute(httpGet); HttpEntity entity = httpResponse.getEntity(); String response = EntityUtils.toString(entity, "utf-8"); //LOGGER.error("获取Token-------------------"+response); Gson token_gson = new Gson(); accessTokenInfo = token_gson.fromJson(response, JsonObject.class); return accessTokenInfo.get("access_token").toString().replaceAll("\"", ""); } catch (Exception ex) { System.out.println("fail to request wechat user info. [error={" + ex + "}]"); } return ""; } /** * 请求微信接口服务,用code换取用户手机号(每个code只能使用一次,code的有效期为5min) * https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/phonenumber/phonenumber.getPhoneNumber.html * * @param code * @param accessToken * @return */ public static JSONObject getPhoneNumber(String code) { final String appID = "wx88c4501ad86e1174"; final String appSecret = "c08515ced6cd60e976028be5d0d43498"; String accessTokenOne = getAccessTokenOne(appID, appSecret); String result = null; try { // 接口调用凭证:accessToken String baseUrl = "https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=" + accessTokenOne; HashMap<String, Object> requestParam = new HashMap<>(); // 手机号调用凭证 requestParam.put("code", code); // 发送post请求读取调用微信接口获取openid用户唯一标识 String jsonStr = JSONUtil.toJsonStr(requestParam); HttpResponse response = HttpRequest.post(baseUrl) .header(Header.CONTENT_ENCODING, "UTF-8") // 发送json数据需要设置contentType .header(Header.CONTENT_TYPE, "application/x-www-form-urlencoded") .body(jsonStr) .execute(); if (response.getStatus() == HttpStatus.SUCCESS) { result = response.body(); } } catch (Exception e) { e.printStackTrace(); } return JSONObject.parseObject(result); } }