本人使用java開(kāi)發(fā)
框架:springboot 1.5.7.RELEASE
本次項(xiàng)目所用的服務(wù)器已有其他項(xiàng)目在運(yùn)行记舆,并且占用了443端口涨薪。原項(xiàng)目使用nginx實(shí)現(xiàn)轉(zhuǎn)發(fā)芯肤,所以需要重新配置nginx增加域名轉(zhuǎn)發(fā)潜支,以滿足微信必須使用443端口要求蒸矛。
原nginx配置文件如下
user www www;
worker_processes auto;
error_log /www/wwwlogs/nginx_error.log crit;
pid /www/server/nginx/logs/nginx.pid;
worker_rlimit_nofile 51200;
events
{
use epoll;
worker_connections 51200;
multi_accept on;
}
http
{
include mime.types;
include proxy.conf;
#include luawaf.conf;
default_type application/octet-stream;
server_names_hash_bucket_size 512;
client_header_buffer_size 32k;
large_client_header_buffers 4 32k;
client_max_body_size 50m;
sendfile on;
tcp_nopush on;
keepalive_timeout 60;
tcp_nodelay on;
fastcgi_connect_timeout 300;
fastcgi_send_timeout 300;
fastcgi_read_timeout 300;
fastcgi_buffer_size 64k;
fastcgi_buffers 4 64k;
fastcgi_busy_buffers_size 128k;
fastcgi_temp_file_write_size 256k;
fastcgi_intercept_errors on;
gzip on;
gzip_min_length 1k;
gzip_buffers 4 16k;
gzip_http_version 1.1;
gzip_comp_level 2;
gzip_types text/plain application/javascript application/x-javascript text/javascript text/css application/xml;
gzip_vary on;
gzip_proxied expired no-cache no-store private auth;
gzip_disable "MSIE [1-6]\.";
limit_conn_zone $binary_remote_addr zone=perip:10m;
limit_conn_zone $server_name zone=perserver:10m;
server_tokens off;
access_log off;
server
{
listen 888;
server_name www.bt.cn;
index index.html index.htm index.php;
root /www/server/phpmyadmin;
#error_page 404 /404.html;
include enable-php.conf;
location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$
{
expires 30d;
}
location ~ .*\.(js|css)?$
{
expires 12h;
}
location ~ /\.
{
deny all;
}
access_log /www/wwwlogs/access.log;
}
include /www/server/panel/vhost/nginx/*.conf;
}
配置文件最下面 include 這行內(nèi)容娄徊,會(huì)將*.conf文件全部引入到配置中闽颇。所以我們?cè)?/www/server/panel/vhost/nginx/ 路徑下新增本次項(xiàng)目所需配置,如下
server
{
listen 80;
listen 443 ssl;
server_name 項(xiàng)目域名;
root /根路徑;
#SSL-START SSL相關(guān)配置,請(qǐng)勿刪除或修改下一行帶注釋的404規(guī)則
#error_page 404/404.html;
#HTTP_TO_HTTPS_START
if ($server_port !~ 443){
rewrite ^(/.*)$ https://$host$1 permanent;
}
#HTTP_TO_HTTPS_END
ssl_certificate /ssl證書.crt;
ssl_certificate_key /ssl證書.rsa;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
error_page 497 https://$host$request_uri;
#SSL-END
#REWRITE-START URL重寫規(guī)則引用,修改后將導(dǎo)致面板設(shè)置的偽靜態(tài)規(guī)則失效
#REWRITE-END
location = /xxx.txt{
}
location / {
proxy_pass https://項(xiàng)目域名:項(xiàng)目端口;
}
access_log /www/wwwlogs/項(xiàng)目域名.log;
error_log /www/wwwlogs/項(xiàng)目域名.error.log;
}
配置文件中的 需要替換成自己的內(nèi)容橄仆。
location中的xxx.txt 是在上一篇中說(shuō)的配置授權(quán)域名時(shí)的校驗(yàn)文件剩膘,將校驗(yàn)文件放置根目錄下既可以訪問(wèn)到。
1.獲取openid
本次項(xiàng)目集成了微信支付盆顾、公眾號(hào)模板消息怠褐,需要使用到用戶openid,所以首先獲取openid您宪。
配置公眾號(hào)菜單 打開(kāi) https://項(xiàng)目域名/open/openid 地址奈懒。
該接口重定向至微信授權(quán)地址奠涌,只需要openid所以使用用戶無(wú)感的 scope=snsapi_base 即可。
代碼如下
@Controller
@Slf4j
public class LoginUserController {
@Resource
private ICourseService courseService;
@Resource
private MyWXPayConfig myWXPayConfig;
@Resource
private RestTemplate restTemplate;
@Resource
private IWxAccessTokerService accessTokerService;
@Resource
private CourseLocationMapper locationMapper;
@Autowired
private IDataDictionaryService dataDictionaryService;
@Value("${server.port}")
private String port;
@Value("${wxconfig.domain}")
private String domain;
@RequestMapping("/open/openid")
public void getOpenId(HttpServletRequest request, HttpServletResponse response){
StringBuffer sb = new StringBuffer();
StringBuffer encodeUrl = new StringBuffer("https://");
//公眾號(hào)中配置的回調(diào)域名(網(wǎng)頁(yè)授權(quán)回調(diào)域名)
String root = request.getContextPath();
String appId = myWXPayConfig.getAppID();
sb.append("https://open.weixin.qq.com/connect/oauth2/authorize?appid=");
sb.append(appId);
try {
//對(duì)重定向url進(jìn)行編碼筐赔,官方文檔要求
encodeUrl.append(domain).append(root)/*.append(":").append(port)*/;
String url = URLEncoder.encode(encodeUrl.toString(), "utf-8");
sb.append("&redirect_uri=").append(url);
//網(wǎng)頁(yè)授權(quán)的靜默授權(quán)snsapi_base
sb.append("&response_type=code&scope=snsapi_base#wechat_redirect");
response.sendRedirect(sb.toString());
} catch (UnsupportedEncodingException e) {
log.error("重定向url編碼失斚承伞:>>" + e.getMessage());
e.printStackTrace();
} catch (Exception e) {
log.error("response重定向失敗:>>" + e.getMessage());
e.printStackTrace();
}
}
/**
* @Title: login
* @Description: TODO(首頁(yè))
* @param @return 設(shè)定文件
* @return String 返回類型
* @throws
*/
@RequestMapping("/")
public String login(Model model, HttpServletRequest request, HttpServletResponse response) throws BusinessException {
//獲取重定向攜帶的code參數(shù)值
String code = request.getParameter("code");
String currentPageUrl = request.getRequestURL().toString();
if(StringUtils.isNotBlank(code)) {
currentPageUrl = new StringBuffer("https://").append(domain).append("/").append("?code=").append(code).append("&state=").toString();
String openId = null;
if (null == openId) {
/*
* 根據(jù)得到的code參數(shù)茴丰,內(nèi)部請(qǐng)求獲取openId的方法达皿。
*/
openId = getOpenId(request,code);
}
/*
* 將openId保存到session中,當(dāng)其他業(yè)務(wù)獲取openId時(shí)贿肩,
* 可先從session中獲取openId.
*/
request.getSession().setAttribute("openid", openId);
}
try {
String jsapi_ticket = accessTokerService.getWeiXinTicket();
Map<String, String> ret = WxJsapiSignUtil.sign(jsapi_ticket, currentPageUrl);
Map<String, String> wxconfig = new HashMap<>();
wxconfig.put("appId", myWXPayConfig.getAppID());
wxconfig.put("timestamp", ret.get("timestamp"));
wxconfig.put("nonceStr", ret.get("nonceStr"));
wxconfig.put("signature", ret.get("signature"));
model.addAttribute("wxconfig", wxconfig);
log.info("wxconfig ="+wxconfig.toString());
} catch (Exception e) {
log.error(e.toString());
}
return "course/index";
}
//發(fā)送請(qǐng)求峦椰,根據(jù)code獲取openId
private String getOpenId(HttpServletRequest request,String code) {
String content = "";
String openId = "";
//封裝獲取openId的微信API
StringBuffer url = new StringBuffer();
url.append("https://api.weixin.qq.com/sns/oauth2/access_token?appid=")
.append(myWXPayConfig.getAppID())
.append("&secret=")
.append(myWXPayConfig.getAppsecret())
.append("&code=")
.append(code)
.append("&grant_type=authorization_code");
try {
content = restTemplate.getForEntity(url.toString(), String.class).getBody();
JSONObject json = JSONObject.parseObject(content);
openId = json.getString("openid");
} catch (Exception e) {
log.error("http獲取openId請(qǐng)求失敗:", e);
}
return openId;
}
}
2.發(fā)送模板消息
以發(fā)送公眾號(hào)模板消息為例汰规,展示openid的使用汤功。
第一步 獲取accessToken
@Service
@Slf4j
public class WxAccessTokenServiceImpl implements IWxAccessTokerService {
private static AccessToken accessToken = null;
@Resource
private MyWXPayConfig wxPayConfig;
@Autowired
private RestTemplate restTemplate;
/**
* 獲取access_token的接口地址
*/
public final static String access_token_url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";
/**
* 通過(guò)APPID 和 APPSECRET
* 獲取assess_token
* @return
*/
@Override
public AccessToken getAccessToken() throws BusinessException {
String appid = wxPayConfig.getAppID();
String appsecret = wxPayConfig.getAppsecret();
if(accessToken == null
|| DateUtils.addSeconds(accessToken.getReceiveTime(), accessToken.getExpires_in()).before(new Date())) {
String requestUrl = access_token_url.replace("APPID", appid).replace("APPSECRET", appsecret);
JSONObject jsonObject = restTemplate.getForObject(requestUrl,JSONObject.class);
// 如果請(qǐng)求成功
if (null != jsonObject && jsonObject.get("access_token")!=null) {
try {
accessToken = new AccessToken();
accessToken.setAccess_token(jsonObject.getString("access_token"));
accessToken.setExpires_in(jsonObject.getInteger("expires_in"));
accessToken.setReceiveTime(new Date());
log.info("獲取token成功!");
} catch (JSONException e) {
accessToken = null;
// 獲取token失敗
log.error("獲取token失敗 errcode:{} errmsg:{}", jsonObject.getInteger("errcode"), jsonObject.getString("errmsg"));
}
} else {
throw new BusinessException(ResponseConstants.FAIL.getRetCode(), "獲取access_token失敗!");
}
} else {
log.info("token在有效期內(nèi),直接返回!");
}
return accessToken;
}
}
@Data
public class AccessToken {
/**
* 獲取到的憑證
*/
private String access_token;
/*
* 憑證得到時(shí)間
*/
private Date receiveTime;
/**
* 憑證有效時(shí)間,單位:秒
*/
private int expires_in;
}
第二步 構(gòu)建模板參數(shù)實(shí)體類
/**
* 微信模板類
*/
@Data
@ToString
public class WeChatTemplate implements Serializable {
private static final long serialVersionUID = 612571563869874653L;
/**
* 模板id
*/
private String template_id;
/**
* 接收者 openId
*/
private String touser;
/**
* 模板跳轉(zhuǎn)鏈接
*/
@JsonSerialize(include= JsonSerialize.Inclusion.NON_EMPTY)
private String url;
/**
* data的數(shù)據(jù)
*/
private TreeMap<String, TreeMap<String, String>> data;
/**
* data 里的數(shù)據(jù)
*
* @param value :模板參數(shù)
* @param color :顏色 可選
* @return
*/
public static TreeMap<String, String> item(String value, String color) {
TreeMap<String, String> params = new TreeMap<String, String>();
params.put("value", value);
params.put("color", color==null?"#173177":color);
return params;
}
}
第三步 模板消息發(fā)送服務(wù)
@Component
@Slf4j
public class WeChatTemplateMessageService {
@Autowired
private RestTemplate restTemplate;
@Resource
private WechatTemplateMessageLogMapper templateMessageLogMapper;
/**發(fā)送模板消息*/
public static final String SEND_TEMPLATE_MESSAGE = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=ACCESS_TOKEN";
/**
* 發(fā)送模板消息
* @param accessToken
* @param template
* @return
*/
public void sendTemplateMsg(String accessToken, WeChatTemplate template){
log.info("模板消息請(qǐng)求參數(shù)"+ template.toString());
String requestUrl =SEND_TEMPLATE_MESSAGE.replace("ACCESS_TOKEN",accessToken);
JSONObject jsonObject = restTemplate.postForObject(requestUrl,template,JSONObject.class);
log.info("返回jsonObject值:"+jsonObject);
if (null != jsonObject) {
int errorCode = jsonObject.getIntValue("errcode");
//發(fā)送日志
if (0 == errorCode) {
log.info("模板消息發(fā)送成功");
} else {
String errorMsg = jsonObject.getString("errmsg");
log.info("模板消息發(fā)送失敗,錯(cuò)誤是 "+errorCode+",錯(cuò)誤信息是"+ errorMsg);
}
}
}
}
最后 發(fā)送消息代碼
try {
//發(fā)送消息
AccessToken accessToken = wxAccessTokerService.getAccessToken();
if(accessToken == null) {
logger.error("accessToken為空溜哮,無(wú)法發(fā)送消息");
} else {
WechatTemplateMessageConfig config = templateMessageConfigMapper.selectByCode("APPLY");
if(StringUtils.isBlank(config.getTemplate_id())) {
logger.error("模板id未配置滔金,無(wú)法發(fā)送消息");
} else {
String first = "您已報(bào)名參加!";
allData.put("first", WeChatTemplate.item(first, null));
allData.put("keyword1",WeChatTemplate.item(TextFormatUtil.towDecimalPlacesFormat(courseApply.getTotal_fee())+"元",null));
allData.put("keyword2",WeChatTemplate.item(courseUser.getName(),null));
allData.put("keyword3",WeChatTemplate.item(courseUser.getMobile(),null));
allData.put("keyword4",WeChatTemplate.item(TextFormatUtil.timeFormatText(courseApply.getApply_time()),null));
allData.put("keyword5",WeChatTemplate.item("微信公眾號(hào)", null));
allData.put("remark",WeChatTemplate.item("感謝您的參與茂嗓!", null));
WeChatTemplate template = new WeChatTemplate();
template.setTouser(courseUser.getOpenid());
template.setTemplate_id(config.getTemplate_id());
template.setUrl(null);
template.setData(allData);
weChatTemplateMessageService.sendTemplateMsg(accessToken.getAccess_token(), template);
}
}
}catch (Exception e){
e.printStackTrace();
logger.error("發(fā)送消息出現(xiàn)異常"+e.getMessage());
}
至此 獲取openid及發(fā)送模板消息完成餐茵,后續(xù)如有時(shí)間會(huì)完成微信jssdk及微信支付內(nèi)容!如有發(fā)現(xiàn)錯(cuò)誤及不足還請(qǐng)幫忙指正述吸,謝謝忿族!