文章目录
1. 关于网页授权access_token和普通access_token的区别2. 获取用户基础信息3. 微信公众号授权java代码实现微信公众号用户基本信息VO微信公众号AccessTokenVO微信公众号授权代码
4. 获取微信配置信息controller签名处理
5. 获取微信公众号全局唯一接口调用凭据 检查 刷新 封装类
1. 关于网页授权access_token和普通access_token的区别
1、微信网页授权是通过OAuth2.0机制实现的,在用户授权给公众号后,公众号可以获取到一个网页授权特有的接口调用凭证(网页授权access_token),
通过网页授权access_token可以进行授权后接口调用,如获取用户基本信息;
2、其他微信接口,需要通过基础支持中的“获取access_token”接口来获取到的普通access_token调用。
微信公众号的全局唯一接口调用凭据access_token https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Get_access_token.html
通过code换取网页授权access_token https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html
2. 获取用户基础信息
获取用户基本信息有两种方式: 一种通过全局唯一接口调用凭据access_token,调用接口https://api.weixin.qq.com/cgi-bin/user/info?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN 可以获取; 另一种通过获取code,换取的网页授权access_token,调用接口https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN 获取。 两种获取方式获取的信息可参考官方文档:
通过全局唯一接口调用凭据access_token获取 https://developers.weixin.qq.com/doc/offiaccount/User_Management/Get_users_basic_information_UnionID.html#UinonId 通过获取的code,换取网页授权access_token获取 https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html#3
3. 微信公众号授权java代码实现
记录本次微信公众号授权的java代码实现
微信公众号用户基本信息VO
import java
.io
.Serializable
;
import java
.util
.List
;
public class WeChatUserInfoVO implements Serializable {
private Integer subscribe
;
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 Long subscribe_time
;
private String unionid
;
private String remark
;
private Integer groupid
;
private List
<Integer> tagid_list
;
private String subscribe_scene
;
private Long qr_scene
;
private String qr_scene_str
;
private Integer errcode
;
private String errmsg
;
public Integer
getSubscribe() {
return subscribe
;
}
public void setSubscribe(Integer subscribe
) {
this.subscribe
= subscribe
;
}
public String
getOpenid() {
return openid
;
}
public void setOpenid(String openid
) {
this.openid
= openid
;
}
public String
getNickname() {
return nickname
;
}
public void setNickname(String nickname
) {
this.nickname
= nickname
;
}
public Integer
getSex() {
return sex
;
}
public void setSex(Integer sex
) {
this.sex
= sex
;
}
public String
getLanguage() {
return language
;
}
public void setLanguage(String language
) {
this.language
= language
;
}
public String
getCity() {
return city
;
}
public void setCity(String city
) {
this.city
= city
;
}
public String
getProvince() {
return province
;
}
public void setProvince(String province
) {
this.province
= province
;
}
public String
getCountry() {
return country
;
}
public void setCountry(String country
) {
this.country
= country
;
}
public String
getHeadimgurl() {
return headimgurl
;
}
public void setHeadimgurl(String headimgurl
) {
this.headimgurl
= headimgurl
;
}
public Long
getSubscribe_time() {
return subscribe_time
;
}
public void setSubscribe_time(Long subscribe_time
) {
this.subscribe_time
= subscribe_time
;
}
public String
getUnionid() {
return unionid
;
}
public void setUnionid(String unionid
) {
this.unionid
= unionid
;
}
public String
getRemark() {
return remark
;
}
public void setRemark(String remark
) {
this.remark
= remark
;
}
public Integer
getGroupid() {
return groupid
;
}
public void setGroupid(Integer groupid
) {
this.groupid
= groupid
;
}
public List
<Integer> getTagid_list() {
return tagid_list
;
}
public void setTagid_list(List
<Integer> tagid_list
) {
this.tagid_list
= tagid_list
;
}
public String
getSubscribe_scene() {
return subscribe_scene
;
}
public void setSubscribe_scene(String subscribe_scene
) {
this.subscribe_scene
= subscribe_scene
;
}
public Long
getQr_scene() {
return qr_scene
;
}
public void setQr_scene(Long qr_scene
) {
this.qr_scene
= qr_scene
;
}
public String
getQr_scene_str() {
return qr_scene_str
;
}
public void setQr_scene_str(String qr_scene_str
) {
this.qr_scene_str
= qr_scene_str
;
}
public Integer
getErrcode() {
return errcode
;
}
public void setErrcode(Integer errcode
) {
this.errcode
= errcode
;
}
public String
getErrmsg() {
return errmsg
;
}
public void setErrmsg(String errmsg
) {
this.errmsg
= errmsg
;
}
}
微信公众号AccessTokenVO
import java
.io
.Serializable
;
public class WeChatAccessTokenVO implements Serializable {
private String access_token
;
private Integer expires_in
;
private String refresh_token
;
private String openid
;
private String scope
;
private Integer errcode
;
private String errmsg
;
public String
getAccess_token() {
return access_token
;
}
public void setAccess_token(String access_token
) {
this.access_token
= access_token
;
}
public Integer
getExpires_in() {
return expires_in
;
}
public void setExpires_in(Integer expires_in
) {
this.expires_in
= expires_in
;
}
public String
getRefresh_token() {
return refresh_token
;
}
public void setRefresh_token(String refresh_token
) {
this.refresh_token
= refresh_token
;
}
public String
getOpenid() {
return openid
;
}
public void setOpenid(String openid
) {
this.openid
= openid
;
}
public String
getScope() {
return scope
;
}
public void setScope(String scope
) {
this.scope
= scope
;
}
public Integer
getErrcode() {
return errcode
;
}
public void setErrcode(Integer errcode
) {
this.errcode
= errcode
;
}
public String
getErrmsg() {
return errmsg
;
}
public void setErrmsg(String errmsg
) {
this.errmsg
= errmsg
;
}
}
微信公众号授权代码
@Controller
@RequestMapping("/weChatOfficialAccount")
public class WeChatOAController extends BaseController {
private Logger logger
= LoggerFactory
.getLogger(WeChatOAController
.class);
@Value("${wechat.officialAccount.authAccessTokenUrl}")
private String authAccessTokenUrl
;
@Value("${wechat.officialAccount.accessTokenUrl}")
private String accessTokenUrl
;
@Value("${wechat.officialAccount.config.appid}")
private String appid
;
@Value("${wechat.officialAccount.config.secret}")
private String secret
;
@Value("${wechat.officialAccount.config.redirect_uri}")
private String redirect_uri
;
@Autowired
private UserService userService
;
@Resource(name
= "redisTemplate")
private RedisTemplate
<String,String> redisTemplate
;
@RequestMapping(value
= "/oa/login", method
= RequestMethod
.GET
)
@ResponseBody
public ModelAndView
oaLogin(String pageUrl
) {
logger
.info("==pageUrl== " + pageUrl
);
ModelAndView model
= null
;
try {
model
= new ModelAndView();
if(StringUtils
.isBlank(pageUrl
)){
model
.setStatus(HttpStatus
.PRECONDITION_FAILED
);
return model
;
}
URIBuilder url
= new URIBuilder("https://open.weixin.qq.com/connect/oauth2/authorize");
url
.setParameter("appid", appid
);
url
.setParameter("redirect_uri", redirect_uri
+ "?pageUrl=" + pageUrl
);
url
.setParameter("response_type", "code");
url
.setParameter("scope", "snsapi_userinfo");
url
.setParameter("state", "STATE");
System
.out
.println("=====拼接的地址url====== " + "redirect:" + url
.toString() + "&connect_redirect=1#wechat_redirect");
model
.setViewName("redirect:" + url
.toString() + "&connect_redirect=1#wechat_redirect");
} catch (URISyntaxException e
) {
e
.printStackTrace();
}
return model
;
}
@RequestMapping("/oa/auth")
@ResponseBody
public ModelAndView
oaAuth(@RequestParam("code") String code
,String pageUrl
){
logger
.info("==== code:" + code
);
ModelAndView model
= null
;
try {
model
= new ModelAndView();
if(StringUtils
.isAnyEmpty(code
,pageUrl
)){
model
.setStatus(HttpStatus
.PRECONDITION_FAILED
);
return model
;
}
String authTokenResponse
= HttpUtils
.doGet(new URI(authAccessTokenUrl
+String
.format("?appid=%s&secret=%s&code=%s&grant_type=authorization_code",appid
,secret
,code
)));
WeChatAccessTokenVO weChatAccessTokenVO
= JSONObject
.toJavaObject(JSONObject
.parseObject(authTokenResponse
), WeChatAccessTokenVO
.class);
if(weChatAccessTokenVO
.getErrcode() == null
) {
UserDTO dto
= userService
.queryByAccount(weChatAccessTokenVO
.getOpenid());
String accessToken
= weChatAccessTokenVO
.getAccess_token();
String userInfoResponse
= HttpUtils
.doGet(new URI("https://api.weixin.qq.com/sns/userinfo"+String
.format("?access_token=%s&openid=%s&lang=zh_CN",accessToken
,weChatAccessTokenVO
.getOpenid())));
WeChatUserInfoVO weChatUserInfoVO
= JSONObject
.toJavaObject(JSONObject
.parseObject(userInfoResponse
), WeChatUserInfoVO
.class);
if(accessToken
!= null
&& weChatUserInfoVO
!=null
&& weChatUserInfoVO
.getErrcode() == null
) {
logger
.info("微信公众号获取用户信息成功,用户信息:" + JSONObject
.toJSONString(weChatUserInfoVO
));
if(dto
!= null
) {
checkUserStatus(dto
.getStatus());
dto
= userService
.doUpdateUserInfo(weChatUserInfoVO
.getOpenid(), null
, weChatUserInfoVO
.getNickname(), weChatUserInfoVO
.getSex(), null
, null
, null
, null
,null
);
} else {
dto
= new UserDTO();
dto
.setPassword(String
.format("%s-%s", new Object[]{"weChatOfficialAccount", MemberType
.WECHAT_MEMBER
.toChannel()}));
dto
.setAccount(weChatAccessTokenVO
.getOpenid());
dto
.setAccountType(MemberType
.WECHAT_MEMBER
.toValue());
dto
.setAppId(appid
);
dto
.setUnionId(weChatUserInfoVO
.getUnionid());
dto
.setSex(weChatUserInfoVO
.getSex());
dto
.setName("微信公众号用户");
dto
.setNickName(weChatUserInfoVO
.getNickname());
userService
.doBatRegister(dto
);
}
} else {
logger
.info("微信公众号获取用户信息失败");
if(dto
== null
) {
dto
= new UserDTO();
dto
.setPassword(String
.format("%s-%s", new Object[]{"weChatOfficialAccount", MemberType
.WECHAT_MEMBER
.toChannel()}));
dto
.setName("微信公众号用户");
dto
.setAccount(weChatAccessTokenVO
.getOpenid());
dto
.setAccountType(MemberType
.WECHAT_MEMBER
.toValue());
dto
.setAppId(appid
);
dto
.setSex(SexType
.UNKNOW
.toValue());
userService
.doBatRegister(dto
);
}
}
String redirectUrl
= "";
pageUrl
= URLDecoder
.decode(pageUrl
, "utf-8");
pageUrl
= new String(java
.util
.Base64
.getDecoder().decode(pageUrl
));
if (pageUrl
.indexOf("?") == -1) {
redirectUrl
= pageUrl
+ "?headimgurl="+weChatUserInfoVO
.getHeadimgurl() ;
} else {
redirectUrl
= pageUrl
+ "&headimgurl="+weChatUserInfoVO
.getHeadimgurl() ;
}
model
.setViewName("redirect:" + redirectUrl
);
logger
.info("redirectUrl="+redirectUrl
);
return model
;
} else {
logger
.info(String
.format("微信公众号授权失败,%s", authTokenResponse
));
throwApiExp(weChatAccessTokenVO
.getErrcode(), "微信公众号登录失败-" + weChatAccessTokenVO
.getErrmsg());
return null
;
}
} catch (Exception e
) {
logger
.info("微信公众号授权异常",e
);
}
return null
;
}
}
4. 获取微信配置信息
controller
@Controller
@RequestMapping("/weChatOfficialAccount")
public class WeChatOAController extends BaseController {
private Logger logger
= LoggerFactory
.getLogger(WeChatOAController
.class);
@Value("${wechat.officialAccount.accessTokenUrl}")
private String accessTokenUrl
;
@Value("${wechat.officialAccount.config.appid}")
private String appid
;
@Value("${wechat.officialAccount.config.secret}")
private String secret
;
public String jsapi_ticket_url
= "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=%s&type=jsapi";
@Autowired
private UserService userService
;
@Resource(name
= "redisTemplate")
private RedisTemplate
<String,String> redisTemplate
;
@Log
@ApiOperation("获取Config信息")
@PostMapping("/oa/getWxConfig")
public Map
<String, Object> getWxConfig(String url
) {
logger
.info("===获取Config信息 参数 url==== " + url
);
String access_token
= globalAccessTokenCheckRefresh();
String jsapi_ticket
= getTicket(access_token
);
String nonce_str
= WechatSign
.create_nonce_str();
String timestamp
= WechatSign
.create_timestamp();
Map
<String, String> ret
= WechatSign
.sign(jsapi_ticket
,url
,nonce_str
,timestamp
);
Map map
= new HashMap();
map
.put("appId",appid
);
map
.put("timestamp",timestamp
);
map
.put("nonceStr",nonce_str
);
map
.put("signature",ret
.get("signature"));
System
.out
.println("==map==== " + map
);
return ResponseBuilder
.build(map
);
}
public String
globalAccessTokenCheckRefresh(){
try {
String globalAccessTokenKey
= "WECHAT:GLOBALACCESSTOKEN";
String globalAccessToken
= redisTemplate
.opsForValue().get("globalAccessTokenKey");
long globalAccessTokenExpire
= redisTemplate
.getExpire(globalAccessTokenKey
);
if(globalAccessToken
!= null
&& globalAccessTokenExpire
>= 5*60){
return globalAccessToken
;
}
String accessTokenResponse
= HttpUtils
.doGet(new URI(accessTokenUrl
+ String
.format("?grant_type=client_credential&appid=%s&secret=%s",appid
,secret
)));
WeChatAccessTokenVO accessTokenVO
= JSONObject
.toJavaObject(JSONObject
.parseObject(accessTokenResponse
), WeChatAccessTokenVO
.class);
if(accessTokenVO
== null
|| accessTokenVO
.getErrcode() != null
) {
logger
.info(accessTokenResponse
);
return null
;
}
redisTemplate
.opsForValue().set(globalAccessTokenKey
,accessTokenVO
.getAccess_token(),accessTokenVO
.getExpires_in(), TimeUnit
.SECONDS
);
return accessTokenVO
.getAccess_token();
} catch (Exception e
) {
logger
.info("检查刷新微信公众号全局AccessToken异常",e
);
}
return null
;
}
private String
getTicket(String access_token
){
String jsapi_ticket
= "";
if (redisTemplate
.hasKey("jhyj:jsapi_ticket")){
jsapi_ticket
= redisTemplate
.opsForValue().get("jhyj:jsapi_ticket");
}else{
String result
= HttpUtil
.post(String
.format(jsapi_ticket_url
, access_token
), new HashMap<>());
int errcode
= JSONObject
.parseObject(result
).getInteger("errcode");
if (errcode
== 0){
jsapi_ticket
= JSONObject
.parseObject(result
).getString("ticket");
redisTemplate
.opsForValue().set("jhyj:jsapi_ticket",jsapi_ticket
,7000, TimeUnit
.SECONDS
);
}
}
return jsapi_ticket
;
}
}
签名处理
public class WechatSign {
public static Map
<String, String> sign(String jsapi_ticket
, String url
,String nonce_str
,String timestamp
) {
Map
<String, String> ret
= new HashMap<String, String>();
String string1
;
String signature
= "";
string1
= "jsapi_ticket=" + jsapi_ticket
+
"&noncestr=" + nonce_str
+
"×tamp=" + timestamp
+
"&url=" + url
;
System
.out
.println(string1
);
try{
MessageDigest crypt
= MessageDigest
.getInstance("SHA-1");
crypt
.reset();
crypt
.update(string1
.getBytes("UTF-8"));
signature
= byteToHex(crypt
.digest());
}catch (NoSuchAlgorithmException e
) {
e
.printStackTrace();
}catch (UnsupportedEncodingException e
) {
e
.printStackTrace();
}
ret
.put("url", url
);
ret
.put("jsapi_ticket", jsapi_ticket
);
ret
.put("nonceStr", nonce_str
);
ret
.put("timestamp", timestamp
);
ret
.put("signature", signature
);
return ret
;
}
public static void main(String
[] args
) {
Formatter formatter
= new Formatter();
byte b
= 'a';
formatter
.format("|%12x|", b
);
System
.out
.println(formatter
.toString());
formatter
.format("|%02x|",0x5);
String result
= formatter
.toString();
System
.out
.println(result
);
formatter
.close();
}
public static String
byteToHex(final byte[] hash
) {
Formatter formatter
= new Formatter();
for (byte b
: hash
) {
formatter
.format("%02x", b
);
}
String result
= formatter
.toString();
formatter
.close();
return result
;
}
public static String
create_nonce_str() {
return UUID
.randomUUID().toString();
}
public static String
create_timestamp() {
return Long
.toString(System
.currentTimeMillis() / 1000);
}
}
5. 获取微信公众号全局唯一接口调用凭据 检查 刷新 封装类
public class WeChatOAGlobalTokenCheckRefresh {
private static Logger logger
= LoggerFactory
.getLogger(WeChatOAGlobalTokenCheckRefresh
.class);
@Value("${wechat.officialAccount.accessTokenUrl}")
private String accessTokenUrl
;
@Value("${wechat.officialAccount.config.appid}")
private String appid
;
@Value("${wechat.officialAccount.config.secret}")
private String secret
;
@Resource(name
= "redisTemplate")
private RedisTemplate
<String,String> redisTemplate
;
public String
globalAccessTokenCheckRefresh(){
try {
String globalAccessTokenKey
= "WECHAT:GLOBALACCESSTOKEN";
String globalAccessToken
= redisTemplate
.opsForValue().get("globalAccessTokenKey");
long globalAccessTokenExpire
= redisTemplate
.getExpire(globalAccessTokenKey
);
if(globalAccessToken
!= null
&& globalAccessTokenExpire
>= 5*60){
return globalAccessToken
;
}
String accessTokenResponse
= HttpUtils
.doGet(new URI(accessTokenUrl
+String
.format("?grant_type=client_credential&appid=%s&secret=%s",appid
,secret
)));
WeChatAccessTokenVO accessTokenVO
= JSONObject
.toJavaObject(JSONObject
.parseObject(accessTokenResponse
), WeChatAccessTokenVO
.class);
if(accessTokenVO
== null
|| accessTokenVO
.getErrcode() != null
) {
logger
.info(accessTokenResponse
);
return null
;
}
redisTemplate
.opsForValue().set(globalAccessTokenKey
,accessTokenVO
.getAccess_token(),accessTokenVO
.getExpires_in(), TimeUnit
.SECONDS
);
return accessTokenVO
.getAccess_token();
} catch (Exception e
) {
logger
.info("检查刷新微信公众号全局AccessToken异常",e
);
}
return null
;
}
}