https://developers.weixin.qq.com/doc/oplatform/Website_App/WeChat_Login/Wechat_Login.html
<script src="https://res.wx.qq.com/connect/zh_CN/htmledition/js/wxLogin.js">script>
<div id="qrCode">div>
methods:{
// 初始化微信二维码
initQRCode() {
let style = `.old-template{width:100%;height:100%;}
.loginPanel,.impowerBox{width:100%;height:100%;}
.loginPanel{display: flex;flex-direction: column;}
.panelContent{flex:1;height:0;display: flex;flex-direction: column;color:#333;}
.panelContent .wrp_code{flex:1;height:0;display: flex;align-items: center;justify-content: center;}
.panelContent .wrp_code img {margin-top:0;}
.impowerBox .qrcode {width:60%;}
.loginPanel .title {display:none;}
.loginPanel .info {width:100%;display: flex;justify-content: center;align-items: center;}`; //更改二维码样式
let blob = new Blob([style], {type: 'text/css;chartset=utf-8'})
let fileReader = new FileReader();
fileReader.readAsDataURL(blob);
fileReader.onload = e => {
new WxLogin({
id: "qrCode",
appid: 'xxxxxxxxx',
scope: 'snsapi_login',
redirect_uri: encodeURIComponent('http://localhost:8080'), //如果有项目名,在这里进行拼接,微信开放平台设置授权回调域,不用填写项目名
style: 'white',
state: "",
href: e.target.result
})
}
},
// 微信二维码登录
QRCodeService(code) {
ajaxMethods.defaultRequest("weChatOpenPlatform/login", {
code
}, msg => {
const {status, expires_in, userId, username, headimgurl, authority} = msg;
if (authority) {
ajaxMethods.message(status, null, "登录");
if (status) {
commonMethods.setCookies({
userId,
cloudUserName: username,
headimgurl
}, new Date(new Date().getTime() + expires_in));
}
} else {
this.$message.error("您没有权限,请联系管理员")
}
}, null, null, () => {
setTimeout(() => {
location.href = location.origin + location.pathname + location.hash;
}, 1000)
})
}
},
mounted(){
let code = location.search;
code = code.substring(code.lastIndexOf('code=') + 5);
code = code.substring(0, code.lastIndexOf('&'));
if (code) {
this.QRCodeService(code);
} else {
this.initQRCode();
}
}
参数 | 是否必须 | 说明 |
---|---|---|
self_redirect | 否 | true:手机点击确认登录后可以在 iframe 内跳转到 redirect_uri,false:手机点击确认登录后可以在 top window 跳转到 redirect_uri。默认为 false。 |
id | 是 | 第三方页面显示二维码的容器id |
appid | 是 | 应用唯一标识,在微信开放平台提交应用审核通过后获得 |
scope | 是 | 应用授权作用域,拥有多个作用域用逗号(,)分隔,网页应用目前仅填写snsapi_login即可 |
redirect_uri | 是 | 重定向地址,需要进行UrlEncode |
state | 否 | 用于保持请求和回调的状态,授权请求后原样带回给第三方。该参数可用于防止csrf攻击(跨站请求伪造攻击),建议第三方带上该参数,可设置为简单的随机数加session进行校验 |
style | 否 | 提供"black"、"white"可选,默认为黑色文字描述。详见文档底部FAQ |
href | 否 | 自定义样式链接,第三方可根据实际需求覆盖默认样式。详见文档底部FAQ |
https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code
参数 | 是否必须 | 说明 |
---|---|---|
appid | 是 | 应用唯一标识,在微信开放平台提交应用审核通过后获得 |
secret | 是 | 应用密钥AppSecret,在微信开放平台提交应用审核通过后获得 |
code | 是 | 填写第一步获取的code参数 |
grant_type | 是 | 填authorization_code |
正确的返回:
{
"access_token":"ACCESS_TOKEN",
"expires_in":7200,
"refresh_token":"REFRESH_TOKEN",
"openid":"OPENID",
"scope":"SCOPE",
"unionid": "o6_bmasdasdsad6_2sgVt7hMZOPfL"
}
参数 | 说明 |
---|---|
access_token | 接口调用凭证 |
expires_in | access_token接口调用凭证超时时间,单位(秒) |
refresh_token | 用户刷新access_token |
openid | 授权用户唯一标识 |
scope | 用户授权的作用域,使用逗号(,)分隔 |
unionid | 当且仅当该网站应用已获得该用户的userinfo授权时,才会出现该字段。 |
错误返回样例:
{"errcode":40029,"errmsg":"invalid code"}
获取access_token后,进行接口调用,有以下前提:
- access_token有效且未超时;
- 微信用户已授权给第三方应用帐号相应接口作用域(scope)。
对于接口作用域(scope),能调用的接口有以下:
授权作用域(scope) | 接口 | 接口说明 |
---|---|---|
snsapi_base | /sns/oauth2/access_token | 通过code换取access_token、refresh_token和已授权scope |
snsapi_base | /sns/oauth2/refresh_token | 刷新或续期access_token使用 |
snsapi_base | /sns/auth | 检查access_token有效性 |
snsapi_userinfo | /sns/userinfo | 获取用户个人信息 |
#微信操作
wechat:
#开放平台
openPlatform:
appId: xxxxxx
appSecret: xxxxxx
#Get请求 参数?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code
#参数 是否必须 说明
#appid 是 应用唯一标识,在微信开放平台提交应用审核通过后获得
#secret 是 应用密钥AppSecret,在微信开放平台提交应用审核通过后获得
#code 是 填写第一步获取的code参数
#grant_type 是 填authorization_code
accessTokenUrl: https://api.weixin.qq.com/sns/oauth2/access_token
#获取用户信息
userInfoUrl: https://api.weixin.qq.com/sns/userinfo
@RestController
@Api(tags = "微信开放平台操作接口")
@RequestMapping("/weChatOpenPlatform")
public class WeChatOpenPlatformController {
@Autowired
WeChatOpenPlatformService weChatOpenPlatformService;
@ApiOperation(value = "微信扫码登录")
@PostMapping("/login")
@ApiImplicitParam(name = "code", value = "code,用户扫码获取的code", dataTypeClass = String.class, required = true)
public JSONObject login(String code) {
return weChatOpenPlatformService.login(code);
}
@ApiOperation(value = "退出微信登录")
@PostMapping("/logout")
@ApiImplicitParam(name = "userId", value = "用户ID", dataTypeClass = String.class, required = true)
public boolean logout(String userId) {
return weChatOpenPlatformService.logout(userId);
}
}
public interface WeChatOpenPlatformService {
JSONObject login(String code);
boolean logout(String userId);
}
返回结果根据项目自行处理:
@Service
public class WeChatOpenPlatformServiceImpl implements WeChatOpenPlatformService {
@Value("${wechat.openPlatform.appId}")
private String appId;
@Value("${wechat.openPlatform.appSecret}")
private String appSecret;
@Value("${wechat.openPlatform.accessTokenUrl}")
private String accessTokenUrl;
@Value("${wechat.openPlatform.userInfoUrl}")
private String userInfoUrl;
@Autowired
UsersMapper usersMapper;
/**
* 微信扫码登录
*
* @param code code
* @return 用户信息
*/
@Override
public JSONObject login(String code) {
JSONObject tokenJSONObject = getAccessToken(code);
System.out.println(tokenJSONObject);
String access_token = tokenJSONObject.getString("access_token");
JSONObject userInfo = new JSONObject();
userInfo.put("authority", false);
userInfo.put("status", false);
if (access_token != null) {
Integer expires_in = tokenJSONObject.getInteger("expires_in");
String openid = tokenJSONObject.getString("openid");
String unionid = tokenJSONObject.getString("unionid");
ToolUtils.setSession("accessToken", access_token, expires_in);
userInfo = getWeChatUserInfo(access_token, openid);
System.out.println(userInfo);
userInfo.put("expires_in", expires_in);
Users users = new Users();
users.setUnionid(unionid);
List<Users> usersList = usersMapper.unIonIdList(users);
Users users1;
if (usersList.size() > 0) {
users1 = usersList.get(0);
userInfo.put("status", true);
userInfo.put("authority", users1.getStatus());
} else {
users1 = new Users();
users1.setId(UUID.randomUUID().toString());
users1.setUsername(userInfo.getString("nickname"));
users1.setUnionid(unionid);
users1.setPassword(EncryptUtils.MD5("admin"));
int i = usersMapper.insertSelective(users1);
userInfo.put("status", i==1);
}
userInfo.put("userId", users1.getId());
userInfo.put("username", users1.getUsername());
}
return userInfo;
}
/**
* 退出微信登录
*
* @param userId 用户ID
* @return 状态 成功true、则false
*/
@Override
public boolean logout(String userId) {
Users users = usersMapper.selectByPrimaryKey(userId);
if (users != null) {
ToolUtils.removeSession("accessToken");
return true;
}
return false;
}
/**
* 获取token
*
* @param code code
* @return access_token
*/
public JSONObject getAccessToken(String code) {
Map<String, Object> map = new HashMap<>();
map.put("appid", appId);
map.put("secret", appSecret);
map.put("code", code);
map.put("grant_type", "authorization_code");
String params = HttpUtil.get(accessTokenUrl, map);
return JSONObject.parseObject(params);
}
/**
* 获取微信用户信息
*
* @param access_token token
* @param openId openid
* @return 用户信息
*/
public JSONObject getWeChatUserInfo(String access_token, String openId) {
HashMap<String, Object> map = new HashMap<>();
map.put("access_token", access_token);
map.put("openid", openId);
map.put("lang", "zh_CN");
String params = HttpUtil.get(userInfoUrl, map);
return JSONObject.parseObject(params);
}
}
工具类:
@Component
public class ToolUtils {
public static final int COOKIE_DEFAULT_TIME = 1; //以秒为单位(1s)
public static final int SESSION_DEFAULT_TIME = 1; //以秒为单位(1s)
public static HttpServletRequest request;
public static HttpServletResponse response;
public static HttpSession session;
@Autowired
private HttpServletResponse httpServletResponse;
@Autowired
private HttpServletRequest httpServletRequest;
@Autowired
private HttpSession httpSession;
@PostConstruct
public void beforeInit() {
request = httpServletRequest;
response = httpServletResponse;
session = httpSession;
}
/**
* 添加cookie
*
* @param key 名称
* @param value 值
* @param cookieAge 过期时间
*/
public static void setCookie(String key, String value, int cookieAge) {
if (key == null) {
return;
}
if (value == null) {
value = "";
}
Cookie cookie = new Cookie(key, value);
cookie.setPath("/");
if (cookieAge != 0) {
cookie.setMaxAge(COOKIE_DEFAULT_TIME * cookieAge);
} else {
cookie.setMaxAge(COOKIE_DEFAULT_TIME);
}
response.addCookie(cookie);
try {
response.flushBuffer();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 获取cookie
*
* @param cookieName 名称
* @return 值
*/
public static String getCookie(String cookieName) {
Cookie[] cookies = request.getCookies();
String result = null;
if (null != cookies && cookies.length > 0) {
for (Cookie cookie : cookies) {
if (cookie.getName().equals(cookieName)) {
result = cookie.getValue();
break;
}
}
}
return result;
}
/**
* 删除cookie
*
* @param cookieName 名称
* @return 结果
*/
public static boolean removeCookie(String cookieName) {
boolean result = false;
if (null != cookieName) {
Cookie cookie = new Cookie(cookieName, null);
cookie.setMaxAge(0);
cookie.setPath("/");
cookie.setValue("");
response.addCookie(cookie);
result = true;
}
return result;
}
/**
* 设置session
*
* @param key 键
* @param value 值
* @param sessionAge 过期时间
*/
public static void setSession(String key, String value, int sessionAge) {
if (key == null) {
return;
}
if (value == null) {
value = "";
}
session.setAttribute(key, value);
if (sessionAge != 0) {
session.setMaxInactiveInterval(COOKIE_DEFAULT_TIME * sessionAge);
} else {
session.setMaxInactiveInterval(SESSION_DEFAULT_TIME);
}
}
/**
* 获取session
*
* @param sessionName 键
* @return 返回内容
*/
public static String getSession(String sessionName) {
Object attribute = session.getAttribute(sessionName);
return null != attribute ? attribute.toString() : null;
}
/**
* 删除session
*
* @param sessionName 键
*/
public static void removeSession(String sessionName) {
Object attribute = session.getAttribute(sessionName);
if (null != attribute) {
session.removeAttribute(sessionName);
}
}
}
以上代码亲测可用,如有问题欢迎留言!