Java实现微信登录
Posted 闪耀太阳a
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java实现微信登录相关的知识,希望对你有一定的参考价值。
Java实现微信登录
代码实现微信授权
简单来说,微信授权分为四步:
1.授权登录接口,生成一个二维码在页面中。
2.用户点击授权成功后自动获取code。
3.浏览器自动利用code访问回调接口。
4.在回调接口中利用code登录,appId.appSecret进行登录获取token。
5.利用4获取的token再次访问接口INFO_URL 获取用户微信信息
6.再根据用户微信消息,进行本地信息存储,生成token,并将权限角色等信息返回至前端
7.至此整个微信登录过程结束
一.首先获取微信登录跳转地址
/**
* 登录地址
*/
private final static String LOGIN_URL = "https://open.weixin.qq.com/connect/qrconnect?" +
"appid=appId&" +
"redirect_uri=redirect&" +
"response_type=code&" +
"scope=snsapi_login&" +
"state=state";
/**
* 获取微信登录跳转地址
* @return
*/
@RequestMapping(value = "/api/common/wx/get-url", method = RequestMethod.POST)
@ApiOperation(value = "构建PC端微信登录地址", notes = "获取地址后直接跳转到登录地址,用户进行扫码登录")
public ApiRest<WxUrlRespDTO> getUrl(@RequestBody WxUrlReqDTO reqDTO)
// state为登录跳转标识,可以跳转到h5页面
String url = LOGIN_URL
.replace("appId", wechatConfig.getSiteAppId())
// 统一跳转地址
.replace("redirect", baseConfig.getLoginRedirect())
.replace("state", reqDTO.getState());
WxUrlRespDTO respDTO = new WxUrlRespDTO();
respDTO.setUrl(url);
return success(respDTO);
二.扫码成功后会回调该redirect的url
即 “/api/common/wx/redirect” 该回调接口地址
/**
* 网站的微信登录跳转
* @param code
* @return
*/
@GetMapping("/api/common/wx/redirect")
@ApiOperation(value = "微信PC端登录回调地址")
public ApiRest<SysUserLoginDTO> webRedirect(HttpServletResponse response, @RequestParam("code") String code,
@RequestParam("state") String state) throws IOException
// 获取会话
SysUserLoginDTO respDTO = wxLoginService.webLogin(
wechatConfig.getSiteAppId(),
wechatConfig.getSiteAppSecret(),
code);
String redirect = "";
// 跳转到H5版本
if(PlatformType.H5.equals(state))
redirect = baseConfig.getLoginSyncH5();
// 跳转到PC版本
if(PlatformType.PC.equals(state))
redirect = baseConfig.getLoginSyncPc();
// 替换地址
redirect = redirect
.replace("token", respDTO.getToken())
.replace("roleType", String.valueOf(respDTO.getRoleType()));
if(StringUtils.isBlank(redirect))
return super.failure("同步登录参数错误或跳转地址未配置!");
response.sendRedirect(redirect);
return null;
会将扫码授权登录成功时返回的code一并携带,请求该回调地址
在该回调接口中,
// 获取会话wxLoginService.webLogin(wechatConfig.getSiteAppId(),wechatConfig.getSiteAppSecret(), code);
通过该方法真正获取微信相关信息
三.通过该方法wxLoginService.webLogin真正获取本人微信等相关信息
@Override
public SysUserLoginDTO webLogin(String appId, String secret, String code)
//登录成功获取本人微信的相关信息
WxInfoRespDTO rest = LoginUtils.requestInfo(appId, secret, code);
// 登录并返回
return sysUserService.loginByThird(LoginType.WECHAT, rest.getUqId(), rest.getNickName(), rest.getHeadimgurl());
3.1 首先通过该appId,sercet,code,获取微信登录的token信息
/**
* 获取AccessToken
*/
private static final String ACCESS_URL = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=appId&secret=appSecret&code=code&grant_type=authorization_code";
/**
* 获取accessToken
* @param appId
* @param appSecret
* @param code
* @return
*/
public static WxTokenRespDTO requestAccess(String appId, String appSecret, String code)
// 构建完整请求URL
String url = ACCESS_URL.replace("appId", appId)
.replace("appSecret", appSecret)
.replace("code", code);
// 获得返回JSON
String json = HttpClientUtil.getJson(url, null, null);
log.info(json);
// 转换为登录结果
WxTokenRespDTO rest = JSON.parseObject(json, WxTokenRespDTO.class);
return rest;
3.2 利用该token再次请求获取微信用户个人相关信息
/**
* 获取用户信息接口
*/
private static final String INFO_URL = "https://api.weixin.qq.com/sns/userinfo?access_token=token&openid=openid";
/**
* 获取用户头像昵称等内容
* @param appId
* @param appSecret
* @return
*/
public static WxInfoRespDTO requestInfo(String appId, String appSecret, String code)
// 获取accessToken
WxTokenRespDTO token = requestAccess(appId, appSecret, code);
// 获取用户信息
String url = INFO_URL
.replace("token", token.getAccessToken())
.replace("openid", token.getOpenid());
//发送httpGet请求
String json = HttpClientUtil.getJson(url, null, null);
//解析json结果 转化为Java对象
WxInfoRespDTO rest = JSON.parseObject(json, WxInfoRespDTO.class);
return rest;
WxInfoRespDTO 从微信中获取的用户信息:
public class WxInfoRespDTO extends BaseDTO
@ApiModelProperty(value = "会话标识", required=true)
private String openId;
@ApiModelProperty(value = "昵称", required=true)
private String nickName;
@ApiModelProperty(value = "性别", required=true)
private Integer sex;
@ApiModelProperty(value = "省份", required=true)
private String province;
@ApiModelProperty(value = "市", required=true)
private String city;
@ApiModelProperty(value = "国家", required=true)
private String country;
@ApiModelProperty(value = "头像地址", required=true)
private String headimgurl;
@ApiModelProperty(value = "头像地址", required=true)
private String unionid;
@ApiModelProperty(value = "头像地址", required=true)
private List<String> privilege;
拿到微信登录后的相关信息后,进行本项目的登录
四.本项目微信登录后业务代码
@Override
public SysUserLoginDTO loginByThird(String loginType, String openId, String nickName, String avatar)
String userId = sysUserBindService.findBind(loginType, openId);
// 不存在,创建新的用户
if (StringUtils.isBlank(userId))
// 随机产生数据
SysUserLoginDTO dto = this.saveAndLogin(
RandomStringUtils.randomAlphabetic(16),
"A01",
nickName,
avatar,
RandomStringUtils.randomAlphanumeric(32));
// 建立绑定关系
sysUserBindService.save(false, dto.getId(), loginType, openId);
return dto;
// 校验用户状态&密码
SysUser user = this.getById(userId);
return this.checkAndLogin(user, null);
/**
* 用户登录校验
*
* @param user
*/
private SysUserLoginDTO checkAndLogin(SysUser user, String password)
if (user == null)
throw new ServiceException(ApiError.ERROR_90010001);
// 被禁用
if (UserState.DISABLED.equals(user.getState()))
throw new ServiceException(ApiError.ERROR_90010005);
// 待审核
if (UserState.AUDIT.equals(user.getState()))
throw new ServiceException(ApiError.ERROR_90010006);
if (!StringUtils.isBlank(password))
boolean pass = PassHandler.checkPass(password, user.getSalt(), user.getPassword());
if (!pass)
throw new ServiceException(ApiError.ERROR_90010002);
return this.setToken(user);
生成用户的token存储并将用户角色权限等信息,返回至前端
/**
* 保存会话信息
*
* @param user
* @return
*/
@Override
public SysUserLoginDTO setToken(SysUser user)
// 获取一个用户登录的信息
String key = Constant.USER_NAME_KEY + user.getUserName();
String json = redisService.getString(key);
if (!StringUtils.isBlank(json))
// 删除旧的会话
redisService.del(key);
SysUserLoginDTO respDTO = new SysUserLoginDTO();
BeanMapper.copy(user, respDTO);
// 正常状态才登录
if(UserState.NORMAL.equals(user.getState()))
// 根据用户生成Token
String token = JwtUtils.sign(user.getUserName());
respDTO.setToken(token);
// 添加角色信息
this.fillRoleData(respDTO);
// 权限表,用于前端控制按钮
List<String> permissions = sysUserRoleService.findUserPermission(user.getId());
respDTO.setPermissions(permissions);
// 保存如Redis
redisService.set(key, JSON.toJSONString(respDTO));
return respDTO;
到此整个微信登录过程结束
java实现微信小程序服务端(登录)
微信小程序如今被广泛使用,微信小程序按照微信官网的定义来说就是:
微信小程序是一种全新的连接用户与服务的方式,它可以在微信内被便捷地获取和传播,同时具有出色的使用体验。
这就是微信小程序的魅力所在,有的时候我们不需要去下载过多的app,只要打开微信,就可以应用每一个服务,甚至连注册都变得简单起来,具有特别贴近人心的用户体验。
最近在做一个微信小程序的服务端,主要就是实现登录、业务账号和微信号绑定的基本功能,接下来总结一下如何实现微信小程序的服务端代码。
要去实现服务端的代码,就要先去了解客户端的请求。
微信小程序开发平台开发了很全面、功能很强大的开发API供我们使用:
https://developers.weixin.qq.com/miniprogram/dev/api/
我们来看下api中的登录功能:
这个是微信小程序登录的时序图,非常明显地告知了我们微信小程序登录的请求服务调用的过程
1.添加微信小程序固定信息
在注册的时候,这个小程序的appId和appSecret已经知道了,还有固定的url,然后我们可以将其做为配置文件放入后台的代码中去:
2.根据小程序信息和code获取openId和session_key
在前端代码中,每当我们刚进入小程序的时候,都会去调用wx.login()方法,会得到一个code,然后发送给我们,然后,我们通过code和这些已知的参数,去调用微信的接口,去获取openId和session_key,作为登录态的标识
/** * 获取微信小程序的session_key和openid * * @author hengyang4 * @param code 微信前端login()方法返回的code * @return jsonObject * * */ public JSONObject getSessionKeyAndOpenId(String code)throws Exception{ //微信登录的code值 String wxCode = code; //读取属性文件 ResourceBundle resourceBundle = ResourceBundle.getBundle("weixin"); //服务器端调用接口的url String requestUrl = resourceBundle.getString("url"); //封装需要的参数信息 Map<String,String> requestUrlParam = new HashMap<String,String>(); //开发者设置中的appId requestUrlParam.put("appid",resourceBundle.getString("appId")); //开发者设置中的appSecret requestUrlParam.put("secret",resourceBundle.getString("appSecret")); //小程序调用wx.login返回的code requestUrlParam.put("js_code", wxCode); //默认参数 requestUrlParam.put("grant_type", "authorization_code"); JSONObject jsonObject = JSON.parseObject(sendPost(requestUrl,requestUrlParam)); return jsonObject; } /** * 向指定 URL 发送POST方法的请求 * * @param url 发送请求的 URL * @return 所代表远程资源的响应结果 */ public String sendPost(String url, Map<String, ?> paramMap) { PrintWriter out = null; BufferedReader in = null; String result = ""; String param = ""; Iterator<String> it = paramMap.keySet().iterator(); while(it.hasNext()) { String key = it.next(); param += key + "=" + paramMap.get(key) + "&"; } try { URL realUrl = new URL(url); // 打开和URL之间的连接 URLConnection conn = realUrl.openConnection(); // 设置通用的请求属性 conn.setRequestProperty("accept", "*/*"); conn.setRequestProperty("connection", "Keep-Alive"); conn.setRequestProperty("Accept-Charset", "utf-8"); conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); // 发送POST请求必须设置如下两行 conn.setDoOutput(true); conn.setDoInput(true); // 获取URLConnection对象对应的输出流 out = new PrintWriter(conn.getOutputStream()); // 发送请求参数 out.print(param); // flush输出流的缓冲 out.flush(); // 定义BufferedReader输入流来读取URL的响应 in = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8")); String line; while ((line = in.readLine()) != null) { result += line; } } catch (Exception e) { log.error(e.getMessage(),e); } //使用finally块来关闭输出流、输入流 finally{ try{ if(out!=null){ out.close(); } if(in!=null){ in.close(); } } catch(IOException ex){ ex.printStackTrace(); } } return result; }
3. 将openId和session_key生成sessionId返回客户端
第三方服务器端拿到请求回来的session_key和openid,先留着,不能给客户端;然后用操作系统提供的真正随机数算法生成一个新的session,叫session_id
· 所以,每次前端wx.login()后调用的服务端的controller我们就应该这样写:
@RequestMapping(value = "/loginByWeixin", produces = "application/json;charset=UTF-8") public String loginByWeixin(@RequestParam("code") String code) throws Exception{ //得到用户的openId + sessionKey JSONObject jsonObject = getSessionKeyAndOpenId(code); log.info(jsonObject.toString()); System.out.println(jsonObject); String openId = jsonObject.getString("openid"); String sessionKey = jsonObject.getString("session_key"); //组装结果 Map<String,Object> resMap = new HashMap<String,Object>(); //判断openId是否存在用户表中 UserInfo userInfo = userInfoMapper.selectByUserName(openId); if(ValidateUtil.isEmpty(userInfo)){ //不存在该用户关联关系 log.info("验证是否绑定微信","未登录"); resMap.put("code",ErrorCode.ERR_WEIXIN_USER_EMPTY.getErrorCode()); resMap.put("desc",ErrorCode.ERR_WEIXIN_USER_EMPTY.getErrorMessage()); }else { //组装结果 log.info("验证是否绑定微信", "用户查询成功"); //0:操作成功 resMap.put("code", ErrorCode.ERR_SUCCEED.getErrorCode()); resMap.put("desc", ErrorCode.ERR_SUCCEED.getErrorMessage()); resMap.put("userInfo", userInfo); } //将通过md5生成sessionId(一般是用个操作系统提供的真正随机数算法生成新的session) String sessionId = MD5.EncodeByMd5(openId+sessionKey+ DateUtil.getNowTime()); jedisCache.hashSet(sessionId,"ses",openId); resMap.put("sessionId",sessionId); return JSONConvertor.toJSON(resMap); }
4. 使用过滤器过滤请求头中含有session_id的请求
然后我们将session_id为key,微信服务端返回的openId为值,保存起来,这里我们用了redis去缓存这个session信息,openId为我们系统用户与微信绑定的标识
接下来,我们每次请求的时候,都会将session_id放入请求头中去,然后判断在redis中是否有key为该值得键值对,从而判断用户session是否失效
我们这里使用了过滤器来拦截用户请求
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException,ServletException{ //设置跨域请求 HttpServletResponse resp = (HttpServletResponse)response; resp.setHeader("Access-Control-Allow-Origin", "*"); HttpServletRequest req = (HttpServletRequest)request; //获取请求时的sessionId String sessionId = req.getHeader("sessionId"); if(StrUtil.IsNullOrEmpty(sessionId)){ //该请求不需要验证session,直接通过 log.info("sessionId过滤","该请求不需要过滤,通过"); chain.doFilter(request,response); return; }else { //只有在缓存中存在该sessionId才能进行请求 if (!jedisCache.existKey(sessionId)) { // 登录信息已过期,请重新登录 log.info("sessionId过滤", "登录信息失效,请重新登录"); response.getWriter().write("登录信息失效,请重新登录"); return; } log.info("sessionId过滤", "session验证成功"); chain.doFilter(request, response); } }
5. 在数据库中建立用户与微信用户唯一标识的关联关系
每当用户注册或者登陆时,请求中都会含有session_id,然后我们将session_id作为key去redis中查找,得到value值,也就是我们之前存的openId,然后我们将openId和用户信息进行数据库表中的关联,之后,我们调用登陆方法的时候,如果该openId有关联的用户信息,则不需要去登录,直接给前端返回用户信息即可,就是我们之前的那段代码:
在这里,我们的微信小程序服务端代码就基本实现了,可以用于微信小程序与业务系统的登录,微信小程序的功能还有很多,之后我们有机会多去试一下其他功能,感受小程序的强大和快捷。
以上是关于Java实现微信登录的主要内容,如果未能解决你的问题,请参考以下文章
java微信公众号开发。如果要返回超长文本,有没有啥好的实现方式