application.properties文件的配置如下:
## 微信扫码登录
site.web.appid=wx3dfe520b2a
site.web.appkey=e50740f2e8f3608367
site.web.return-url=http://www.xxx.com/wxLoginReturn
package com.shenmazong.shenmablogserver.config;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
/**
* @Description: 全站配置类
* @Author david
* @Date 2021/5/5 9:35
* @Copyright www.shenmazong.com
* @Version V1.0.0
*/
@Data
@Component
public class SiteConfig {
@Value("${site.app.id}")
private String siteAppId;
@Value("${site.shenma.token}")
private String siteShenmaToken;
@Value("${site.code.url}")
private String siteCodeUrl;
@Value("${site.web.appid}")
private String siteWebAppId;
@Value("${site.web.appkey}")
private String siteWebAppKey;
@Value("${site.web.return-url}")
private String siteWebReturnUrl;
}
package com.shenmazong.cms.controller;
import com.shenmazong.cms.config.SiteConfig;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
/**
* @author 军哥
* @version 1.0
* @description: 微信登录控制
* @date 2022/5/13 23:02
*/
@Controller
@Slf4j
public class WxLoginController {
@Autowired
SiteConfig siteConfig;
/**
* @description 获取授权code接口,调用这个接口后,会跳转到一个整屏只有二维码的页面
* @author 军哥
* @date 2022/5/13 23:06
* @version 1.0
*/
@GetMapping(value = "/wxLogin")
public void wxLogin(HttpServletResponse response) throws IOException {
String redirect_url = URLEncoder.encode(siteConfig.getSiteWebReturnUrl(), "UTF-8");
String url = String.format("https://open.weixin.qq.com/connect/qrconnect?appid=%s&redirect_uri=%s&response_type=code&scope=snsapi_login&state=STATE#wechat_redirect",
siteConfig.getSiteWebAppId(),
redirect_url
);
log.info(url);
response.sendRedirect(url);
return;
}
}
/**
* @description 微信登录授权成功后的回调地址
* @author 军哥
* @date 2022/5/13 23:13
* @version 1.0
*/
// https://shenmazong.com/login?code=021Sz3000jNiON15Sl000wO44m1Sz30d&state=STATE
// https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code
@RequestMapping(value = "/wxLoginReturn")
public void wxLoginReturn(HttpServletRequest request, HttpServletResponse response) throws IOException {
String code = request.getParameter("code");
String state = request.getParameter("state");
log.info("wxLoginReturn,code="+code);
String siteCodeUrl = String.format("https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code",
siteConfig.getSiteWebAppId(),
siteConfig.getSiteWebAppKey(),
code);
ResponseEntity<String> responseResultResponseEntity = restTemplate.getForEntity(siteCodeUrl, String.class);
System.out.println("code,"+responseResultResponseEntity.getStatusCodeValue());
if(responseResultResponseEntity.getStatusCodeValue() != 200) {
String url = "https://www.shenmazong.com/login/code/"+responseResultResponseEntity.getStatusCodeValue();
log.info(url);
response.sendRedirect(url);
return;
}
/**
{
"access_token":"56_httrHDFvN9O5OHJFwAVmdC_snbP5gKTvL7TLdV-Wq60nevbvWKvAdRaPDj2QKa0zBpsTltqc_RZ5M1ArBbtztedUzEq4OKZxrsgh7wWWq9s",
"expires_in":7200,
"refresh_token":"56_AEHhOEnjGWQWNnrUBazv4keTv8GLTpURB0G4umER3uv-ZUn4xkuxrDaZn2A8Lc036M4pHCOBOdLW8CxTrjQ74CEz_Bm6PZpQxHSotK4Zdmo",
"openid":"o5d0V6GMavGavfWyPYdXnGICxG6s",
"scope":"snsapi_login",
"unionid":"ozZlq5k-q3dmpxZGwIX5HYwfnp4M"
}
*/
System.out.println(responseResultResponseEntity.getBody());
WxLoginTokenVo wxLoginTokenVo = new ObjectMapper().readValue(responseResultResponseEntity.getBody(), WxLoginTokenVo.class);
String access_token = wxLoginTokenVo.getAccess_token();
String openid = wxLoginTokenVo.getOpenid();
String refresh_token = wxLoginTokenVo.getRefresh_token();
String unionid = wxLoginTokenVo.getUnionid();
// 自动登录
TbUser one = iTbUserService.getOne(new QueryWrapper<TbUser>().lambda().eq(TbUser::getOpenId, openid).last("limit 1"));
if(one != null) {
// 设置登录标志
redisTemplate.opsForValue().set("app_login_code_"+one.getUserCode(), ""+one.getUserCode(), 5, TimeUnit.MINUTES);
//
String url = "https://www.shenmazong.com/login/code/"+one.getUserCode();
log.info(url);
response.sendRedirect(url);
return;
}
// 获取用户信息
// https://api.weixin.qq.com/sns/userinfo?access_token=access_token&openid=openid
siteCodeUrl = String.format("https://api.weixin.qq.com/sns/userinfo?access_token=%s&openid=%s",
access_token, openid);
ResponseEntity<String> userinfoEntity = restTemplate.getForEntity(siteCodeUrl, String.class);
System.out.println("code,"+userinfoEntity.getStatusCodeValue());
if(userinfoEntity.getStatusCodeValue() != 200) {
// 登录失败
String url = "https://www.shenmazong.com/login/code/" + userinfoEntity.getStatusCodeValue();
log.info(url);
response.sendRedirect(url);
return;
}
System.out.println(userinfoEntity.getBody());
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
WxUserInfoVo wxUserInfoVo = objectMapper.readValue(userinfoEntity.getBody(), WxUserInfoVo.class);
String nickname = wxUserInfoVo.getNickname();
Integer sex = wxUserInfoVo.getSex();
String city = wxUserInfoVo.getCity();
String headimgurl = wxUserInfoVo.getHeadimgurl();
String country = wxUserInfoVo.getCountry();
unionid = wxUserInfoVo.getUnionid();
// 获取userCode
String userCode = "";
while (true)
{
userCode = RandomUtils.random().randomIntCode(9);
if(RandomUtils.isMagicCode(Long.valueOf(userCode))) {
continue;
}
//
TbUser user = iTbUserService.getOne(
new QueryWrapper<TbUser>().lambda().eq(TbUser::getUserCode, userCode)
);
if(user == null) {
break;
}
}
// 新建用户
TbUser tbUser = new TbUser();
tbUser.setUserId(IdWorker.getId());
tbUser.setOpenId(openid);
tbUser.setUnionId(unionid);
tbUser.setUserCode(Long.valueOf(userCode));
tbUser.setNickName(nickname);
tbUser.setUserGender(Integer.valueOf(sex));
tbUser.setCity(city);
tbUser.setUserAvatar(headimgurl);
tbUser.setUnionId(unionid);
iTbUserService.save(tbUser);
// 设置登录标志
redisTemplate.opsForValue().set("app_login_code_"+userCode, userCode, 5, TimeUnit.MINUTES);
String url = "https://www.shenmazong.com/login/code/"+tbUser.getUserCode();
log.info(url);
response.sendRedirect(url);
return;
}
在上述代码中,我们需要有一些补充类。
package com.shenmazong.shenmablogserver.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.OkHttp3ClientHttpRequestFactory;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.web.client.RestTemplate;
import java.nio.charset.StandardCharsets;
import java.util.List;
/**
* @Description: 参码配置类
* @Author david
* @Date 2021/5/5 11:39
* @Copyright www.shenmazong.com
* @Version V1.0.0
*/
@Configuration
public class ShenmaConfig {
@Bean
public RestTemplate restTemplate() {
RestTemplate restTemplate = new RestTemplate();
//消息转换器列表
List<HttpMessageConverter<?>> messageConverters = restTemplate.getMessageConverters();
//配置消息转换器StringHttpMessageConverter,并设置utf-8
messageConverters.set(1,
new StringHttpMessageConverter(StandardCharsets.UTF_8));//支持中文字符集,默认ISO-8859-1,支持utf-8
return restTemplate;
}
}
这个配置类有两个作用,一个是用来作为http的工具类,另一个作用是解决页面访问返回数据乱码的问题。
package com.shenmazong.shenmablogserver.vo;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author 军哥
* @version 1.0
* @description: https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code
* @date 2022/5/10 18:59
*/
/**
{
"access_token":"56_httrHDFvN9O5OHJFwAVmdC_snbP5gKTvL7TLdV-Wq60nevbvWKvAdRaPDj2QKa0zBpsTltqc_RZ5M1ArBbtztedUzEq4OKZxrsgh7wWWq9s",
"expires_in":7200,
"refresh_token":"56_AEHhOEnjGWQWNnrUBazv4keTv8GLTpURB0G4umER3uv-ZUn4xkuxrDaZn2A8Lc036M4pHCOBOdLW8CxTrjQ74CEz_Bm6PZpQxHSotK4Zdmo",
"openid":"o5d0V6GMavGavfWyPYdXnGICxG6s",
"scope":"snsapi_login",
"unionid":"ozZlq5k-q3dmpxZGwIX5HYwfnp4M"
}
*/
@Data
@NoArgsConstructor
public class WxLoginTokenVo {
private String access_token;
private Integer expires_in;
private String refresh_token;
private String openid;
private String scope;
private String unionid;
}
package com.shenmazong.cms.vo;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author 军哥
* @version 1.0
* @description: https://api.weixin.qq.com/sns/userinfo?access_token=%s&openid=%s
* @date 2022/5/10 19:22
*/
//{
// "openid":"o5d0V6GMavGavfWyPYdXnGICxG6s",
// "nickname":"",
// "sex":0,
// "language":"",
// "city":"",
// "province":"",
// "country":"",
// "headimgurl":"https:\/\/thirdwx.qlogo.cn\/mmopen\/vi_32\/Q0j4TwGTfTITyu0M64BWia147QUOaWPkcwMSqmxpQRVKSI3KBO2oeGaot0Cko2RcOWPEP38dQfZbKr511TpvLnw\/132",
// "privilege":[],
// "unionid":"ozZlq5k-q3dmpxZGwIX5HYwfnp4M"
//}
@Data
@NoArgsConstructor
public class WxUserInfoVo {
private String openid;
private String nickname;
private Integer sex;
private String language;
private String city;
private String province;
private String country;
private String headimgurl;
private String unionid;
}