微信小程序登录和普通后台系统登录不一样,因为这并不是你输入账户密码然后点击登录就登录了,而是通过微信授权,去换取openid,session_key进行唯一标识确定和获取微信用户数据。
微信小程序前端请参考链接: 微信小程序前端登录.
微信授权登录流程图
话不多说,现在上后端代码 特别注意解密的时候用post请求不然会有转义字符而导致解密失败
yml 配置
weixin:
appid: '你们公司的appid'
secret: '你们公司的secret'
access-token-base-url: 'https://api.weixin.qq.com/sns/jscode2session?'
<!-- 解密微信小程序data-->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.57</version>
</dependency>
@Autowired
WxUserService wxUserService;
// private String phoneCode="";
@Value("${weixin.appid}")
private String appid;
@Value("${weixin.secret}")
private String secret;
@Value("${weixin.access-token-base-url}")
private String wxAccessTokenBaseUrl;
@Autowired
WxUserMapper wxUserMapper;
@Autowired
JwtProcessor jwt;
/**
* 授权
* @return
*/
@GetMapping("/auth")
public JSONObject loginAppkey(WxUserModel wxUser,HttpServletResponse resp) throws UploaderTypeNotExistException, IOException, FileMetaNotValidException {
Assert.notNull(wxUser.getCode(),"code不能为空");
JSONObject jsonObject = new JSONObject();
RestTemplate restTemplate = new RestTemplate();
String url = wxAccessTokenBaseUrl+"appid="+appid+"&secret="+secret+"&js_code="+wxUser.getCode()+"&grant_type=authorization_code";
ResponseEntity forEntity1 = restTemplate.getForEntity(url, String.class);
JSONObject parse = JSONObject.parseObject(forEntity1.getBody().toString());
// parse解析出errcode说明code已经过期
Assert.isNull(parse.get("errcode"),"临时code无效!");
/** 这里看数据库有没有存openid如果存在则直接登录,如果没有则将session_key和openid返给小程序,
如果你的小程序只是不需要绑定手机号则下面快捷登录的不需要,你只需要把唯一凭证openid存入数据库然
后给前端 返个token就OK了
*/
WxUserModel openid = wxUserMapper.selectOpenid(parse.get("openid").toString());
// 存在openid直接登录
if(openid!=null){
UserIdentity ui = new UserIdentity();
ui.setAccountName(openid.getPhone());
String token = jwt.generateToken(ui);
resp.setHeader("Authorization", token);
jsonObject.put("wx",openid);
}else{
jsonObject.put("wx",parse);
}
return jsonObject;
}
/**
* 快捷登录 这相当于注册,将用户信息保存到数据库
* @param sessionKey 这个是微信小程序端传code的时候通过请求微信服务器会生成的一个sessionkey和openid,这里传是为了后面解码用户信息要用到
* @param openid 这个是为了确定用户唯一标识
* @param encryptedData 小程序获取用户信息加密信息比如手机号..
* @param vi 小程序加密信息
* @return
*/
@PostMapping("/quickLogin")
public JSONObject login(String sessionKey,String vi,String encryptedData,String openid,HttpServletResponse resp) throws IOException {
JSONObject jsonObject = new JSONObject();
jsonObject.put("wu",wxUserService.addWXuser(wxUser,resp));
return jsonObject;
}
service层
/**
* 添加微信用户信息
* @param wxUserModel
*/
public WxUserModel addWXuser(WxUserModel wxUserModel,HttpServletResponse resp) throws IOException {
WxUserModel wu = null;
// 查看openid是否存在
wu = wxUserMapper.selectOpenid(wxUserModel.getOpenid());
if(wu==null){
// 这里通过解码之前小程序传过来的加密数据进行解密,获取微信用户的数据
JSONObject s = WechatDecryptDataUtil.getPhoneNumber(wxUserModel.getSessionKey(),wxUserModel.getEncryptedData(), wxUserModel.getVi());
String phoneNumber = s.get("phoneNumber").toString();
WxUserModel w = wxUserMapper.selectPhone(phoneNumber);
Assert.isNull(w,"该手机号已绑定,请换手机号");
wu = new WxUserModel();
wu.setOpenid(wxUserModel.getOpenid());
wu.setPhone(phoneNumber);
wxUserMapper.insert(wu);
}
UserIdentity ui = new UserIdentity();
ui.setAccountName(wu.getPhone());
String token = jwt.generateToken(ui);
resp.setHeader("Authorization", token);
return wu;
}
解码工具类
package util;
import com.alibaba.fastjson.JSONObject;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.encoders.Base64;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.security.AlgorithmParameters;
import java.security.Key;
import java.security.Security;
public class WechatDecryptDataUtil {
//解析电话号码
public static JSONObject getPhoneNumber(String session_key, String encryptedData, String iv) throws IOException {
System.out.println(session_key);
// 这个是为了前端传过来可能会有转义字符变成空所以替换
String s = encryptedData.replaceAll(" ", "+");
byte[] dataByte = org.bouncycastle.util.encoders.Base64.decode(s);
// 加密秘钥
byte[] keyByte = org.bouncycastle.util.encoders.Base64.decode(session_key);
// 偏移量
byte[] ivByte = org.bouncycastle.util.encoders.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 JSONObject.parseObject(result);
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}