1. 前言介紹這個!截至到19年11月我已經(jīng)工作6年了,從業(yè)Java但也折騰過C#、搞PHP也弄過中繼器、IO板卡,似乎我就是一個很喜歡在技術(shù)上折騰的人!與此同時,我也搞了6年的個人小網(wǎng)站,它們的呈現(xiàn)形式多種多樣;有用PHP自己搗鼓的技術(shù)站用于分享資料、書籍、軟件等、有用PHPWIND和DISCUZ的搭建的個人論壇、有用emlog和wordpress搭建的個人博客、也有借用于github+hexo/jekyll的能力組裝出的技術(shù)博客。但無一例外它們都戰(zhàn)死于征戰(zhàn)的路上了,亡于;org域名不能備案、PHP服務(wù)器癱瘓被清空、http連接被注入惡意內(nèi)容、定位不準(zhǔn)確經(jīng)常換模式、缺少核心優(yōu)質(zhì)內(nèi)容等等。但!老衲的心依然如春(cun),因為喜歡干一件事,往往來自于干一了件喜歡的事! 所以!從19年開始我又繼續(xù)寫博客了,注冊了新的域名,備了案、買了服務(wù)器、還喊了新的口號;沉淀、分享、成長,讓自己和他人都能有所收獲!并且將塵封已久的微信公眾號找回;結(jié)構(gòu)上調(diào)整、內(nèi)容上布局、粉絲上求關(guān)注。在這期間遇到了更大的牛;小灰、王二哥、純潔的微笑還有松哥等一群伙伴,從他們那學(xué)到很多知識,真的非常感謝! 那么!這次的產(chǎn)品功能總結(jié)一句話就是;將基于Github+Jekyll搭建的靜態(tài)博客與我并未開發(fā)過的微信公眾號功能打通,通過在文章短口令碼加鎖引導(dǎo)用戶到公眾號內(nèi)回復(fù)密碼可解鎖內(nèi)容,以此來獲得粉絲關(guān)注,當(dāng)然如果取消關(guān)注了則文章繼續(xù)鎖定。 在多說一句,我理解的產(chǎn)品;其實是使用研發(fā)技術(shù)力搭建出可以用于承載接收用戶在各種設(shè)備上所生產(chǎn)的行為數(shù)據(jù)的一種產(chǎn)品化服務(wù)。所以有些產(chǎn)品在做減法,同時也有為豐富的功能做加法,但究其一點我們其實都是在為接收有價值數(shù)據(jù)服務(wù)的。興衰存亡,皆在核心數(shù)據(jù)沉淀與運作上! 2. 流程設(shè)計為了使博客粉絲主動關(guān)注微信公眾號,我們在用戶初次瀏覽文章時增加權(quán)限驗證,給每一個用戶都生成一個唯一碼引導(dǎo)用戶在公眾號內(nèi)回復(fù)解鎖文章,以此來與微信openid對應(yīng)。當(dāng)用戶取消關(guān)注時則進(jìn)行刪除openid或標(biāo)記狀態(tài),使得用戶無法繼續(xù)瀏覽文章。其實為了更好的體驗,我參照了大牛的方式內(nèi)容60%的區(qū)域可見,其余內(nèi)容漸進(jìn)遮擋,蒙朧朧的感覺還挺美。整體流程圖如下; 3. 功能實現(xiàn)為了實現(xiàn)本產(chǎn)品功能,我準(zhǔn)備了;
3.1 前端前端主要負(fù)責(zé)針對發(fā)布時設(shè)置了look: need的文章,在用戶瀏覽文章檢查是否有權(quán)限查看全部內(nèi)容,當(dāng)用戶沒有權(quán)限時隱藏文章60%內(nèi)容,并通過頁面結(jié)尾提醒用戶在公眾號內(nèi)回復(fù)口令解鎖文章。
<article class="post container need lock" itemscope="" itemtype="http:///BlogPosting" style="height: 4400px;"><div class="row"><div class="col-md-9 markdown-body"><h2 id="前言介紹">前言介紹</h2><p>為什么會有路由層?因為在微服務(wù)架構(gòu)設(shè)計中,往往并不會直接將服務(wù)暴漏給調(diào)用端,而是通過調(diào)用路由層進(jìn)行業(yè)務(wù)隔離,以達(dá)到不同的業(yè)務(wù)調(diào)用對應(yīng)的服務(wù)模塊。</p><p><strong>Spring Cloud Zuul</strong></p>
// 文章所在容器的選擇器var articleSelector = 'article.post.container.need';// 找到文章所在的容器var $article = $(articleSelector);// 文章的實際高度var article = $article[0], height = article.clientHeight;// 文章隱藏后的高度var halfHeight = height * 0.4;// 隱藏縮小$article.css('height', halfHeight + 'px');$article.addClass('lock');
.asb-post-01 {
position: absolute;
left: 0;
bottom: 0;
width: 100%;
display: none;
z-index: 10000;margin-bottom: 0;}.asb-post-01 .mask {
height: 240px;
width: 100%;
background: -webkit-gradient(linear, 0 top, 0 bottom, from(rgba(255, 255, 255, 0)), to(#fff));}
UM_distinctid = 16e9cd64925334-0882eb883c9554-7711b3e-144000-16e9cd6492631c function getCookie(name) {var value = "; " + document.cookie;var parts = value.split("; " + name + "=");if (parts.length == 2)return parts.pop().split(";").shift();}function getToken() {let value = getCookie('UM_distinctid');if (!value) {return getUUID().toUpperCase();}return value.substring(value.length - 6).toUpperCase();}
// 查詢后端的結(jié)果var _detect = function() {console.info(token);$.ajax({url : 'https:///xx/xx/check',type: "GET",dataType: "text",data : {token : token},success : function(data) {console.log(data);if (data == 'refuse') {_lock();} else {_unlock();}},error : function(data) {_unlock();}})}// 定時任務(wù)_detect();setInterval(function() {_detect();}, 5000);
3.2 后端
開發(fā)環(huán)境
工程代碼
itstack-ark-wx └── src ├── main │ ├── java │ │ └── org.itstack.demo │ │ ├── application │ │ │├── UserLockAuthService.java │ │ │├── WxReceiveService.java │ │ │└── WxValidateService.java │ │ ├── domain │ │ │├── lockauth │ │ ││ ├── repository │ │ ││ │ └── IUserAuthPatrolRepository.java │ │ ││ └── service │ │ ││ └── UserLockAuthServiceImpl.java │ │ │├── receive │ │ ││ ├── model │ │ ││ │ ├── BehaviorMatter.java │ │ ││ │ └── MessageTextEntity.java │ │ ││ ├── repository │ │ ││ │ └── IUserAuthGrantRepository.java │ │ ││ └── service │ │ ││ ├── engine │ │ ││ │ ├── impl │ │ ││ │ │└── MsgEngineHandle.java │ │ ││ │ ├── Engine.java │ │ ││ │ ├── EngineBase.java │ │ ││ │ └── EngineConfig.java │ │ ││ ├── logic │ │ ││ │ ├── impl │ │ ││ │ │├── AnswerFilter.java │ │ ││ │ │├── SubscribeFilter.java │ │ ││ │ │├── UnlockFilter.java │ │ ││ │ │└── UnsubscribeFilter.java │ │ ││ │ └── LogicFilter.java │ │ ││ └── WxReceiveServiceImpl.java │ │ │└── validate │ │ │ └── service │ │ │ └── WxValidateServiceImpl.java │ │ ├── infrastructure │ │ │├── common │ │ ││ └── Constants.java │ │ │├── dao │ │ ││ └── UserAuthDao.java │ │ │├── po │ │ ││ └── UserAuth.java │ │ │├── repository │ │ ││ ├── UserAuthGrantRepository.java │ │ ││ └── UserAuthPatrolRepository.java │ │ │└── util │ │ │ ├── sdk │ │ │ │ └── SignatureUtil.java │ │ │ └── XmlUtil.java │ │ ├── interfaces │ │ │├── BlogController.java │ │ │└── WxPortalController.java │ │ └── WxApplication.java │ └── resources │ ├── mybatis │ └── application.yml └── test └── java └── org.itstack.ark.wx.test └── ApiTest.java
CREATE TABLEuser_auth(id bigint NOT NULL AUTO_INCREMENT,openId VARCHAR(64),token VARCHAR(16) NOT NULL,uuid VARCHAR(128),createTime DATETIME,updateTime DATETIME,PRIMARY KEY (id, token),CONSTRAINT idx_uuid UNIQUE (uuid))ENGINE=InnoDB DEFAULT CHARSET=utf8 講解部分重點代碼塊,完整代碼下載關(guān)注公眾號;bugstack蟲洞棧 & 回復(fù):itstack-ark-wx interfaces接口層
/**
* 微信公眾號:bugstack蟲洞棧
* 純潔版博客:https://
* 沉淀、分享、成長,讓自己和他人都能有所收獲!
* Create by 付政委 on @2019
*/@RestController@RequestMapping("/wx/portal/{appid}")public class WxPortalController {private Logger logger = LoggerFactory.getLogger(WxPortalController.class);@Autowiredprivate WxValidateService wxValidateService;@Autowiredprivate WxReceiveService wxReceiveService;/**
* 處理微信服務(wù)器發(fā)來的get請求,進(jìn)行簽名的驗證
* <p>
* appid 微信端AppID
* signature 微信端發(fā)來的簽名
* timestamp 微信端發(fā)來的時間戳
* nonce 微信端發(fā)來的隨機(jī)字符串
* echostr 微信端發(fā)來的驗證字符串
*/@GetMapping(produces = "text/plain;charset=utf-8")public String validate(@PathVariable String appid, @RequestParam(value = "signature", required = false) String signature, @RequestParam(value = "timestamp", required = false) String timestamp, @RequestParam(value = "nonce", required = false) String nonce, @RequestParam(value = "echostr", required = false) String echostr) {try {logger.info("微信公眾號驗簽信息{}開始 [{}, {}, {}, {}]", appid, signature, timestamp, nonce, echostr);if (StringUtils.isAnyBlank(signature, timestamp, nonce, echostr)) {throw new IllegalArgumentException("請求參數(shù)非法,請核實!");}boolean check = wxValidateService.checkSign(signature, timestamp, nonce);logger.info("微信公眾號驗簽信息{}完成 check:{}", appid, check);if (!check) return null;return echostr;} catch (Exception e) {logger.error("微信公眾號驗簽信息{}失敗 [{}, {}, {}, {}]", appid, signature, timestamp, nonce, echostr, e);return null;}}/**
* 此處是處理微信服務(wù)器的消息轉(zhuǎn)發(fā)的
*/@PostMapping(produces = "application/xml; charset=UTF-8")public String post(@PathVariable String appid, @RequestBody String requestBody, @RequestParam("signature") String signature, @RequestParam("timestamp") String timestamp, @RequestParam("nonce") String nonce, @RequestParam("openid") String openid, @RequestParam(name = "encrypt_type", required = false) String encType, @RequestParam(name = "msg_signature", required = false) String msgSignature) {try {logger.info("接收微信公眾號信息請求{}開始 {}", openid, requestBody);MessageTextEntity message = XmlUtil.xmlToBean(requestBody, MessageTextEntity.class);BehaviorMatter behaviorMatter = new BehaviorMatter();behaviorMatter.setOpenId(openid);behaviorMatter.setFromUserName(message.getFromUserName());behaviorMatter.setMsgType(message.getMsgType());behaviorMatter.setContent(message.getContent());behaviorMatter.setEvent(message.getEvent());behaviorMatter.setCreateTime(new Date(Long.parseLong(message.getCreateTime()) * 1000L));// 處理消息String result = wxReceiveService.doReceive(behaviorMatter);logger.info("接收微信公眾號信息請求{}完成 {}", openid, result);return result;} catch (Exception e) {logger.error("接收微信公眾號信息請求{}失敗 {}", openid, requestBody, e);return "";}}}
/**
* 微信公眾號:bugstack蟲洞棧
* 純潔版博客:https://
* 沉淀、分享、成長,讓自己和他人都能有所收獲!
* Create by 付政委 on @2019
*/@CrossOrigin("https://")@RestController@RequestMapping("/api")public class BlogController {private Logger logger = LoggerFactory.getLogger(BlogController.class);@Autowiredprivate UserLockAuthService userLockAuthService;@GetMapping(value = "check", produces = "application/json;charset=utf-8")public String check(@RequestParam String token) {try {logger.info("校驗博客瀏覽用戶授權(quán)狀態(tài){}開始", token);boolean status = userLockAuthService.checkAuth(token);logger.info("校驗博客瀏覽用戶授權(quán)狀態(tài){}完成", token, status);return status ? "success" : "refuse";} catch (Exception e) {logger.error("校驗博客瀏覽用戶授權(quán)狀態(tài){}失敗", token, e);return "refuse";}}} application應(yīng)用層
public interface UserLockAuthService {boolean checkAuth(String token);} domain領(lǐng)域?qū)?/strong>
public interface LogicFilter {String filter(BehaviorMatter request);}
@Service("subscribe")public class SubscribeFilter implements LogicFilter {private final String content = "您好!\n" +"\n" +"非常感謝您關(guān)注,微信公眾號:bugstack蟲洞棧 | 也期待您分享給更多小伙伴!\n" +"\n" +"bugstack蟲洞棧,專注于原創(chuàng)技術(shù)專題案例,以最易學(xué)習(xí)編程開發(fā)的方式分享技術(shù)知識,讓萌新、小白、大牛都能有所收獲。目前已經(jīng)完成的專題有;《Netty4.x從入門到實戰(zhàn)》、《手寫RPC框架》、《用Java實現(xiàn)JVM》、《基于JavaAgent的全鏈路監(jiān)控》、《DDD專題案例》,其他更多專題還在排兵布陣中。\n" +"\n" +"獲取專題案例源碼回復(fù);netty案例、rpc案例、用Java實現(xiàn)jvm源碼、基于JavaAgent的全鏈路監(jiān)控案例、DDD落地。\n" +"\n" +"聯(lián)系作者:付政委 | monkeycode";@Overridepublic String filter(BehaviorMatter request) {return content;}}
@Service("msgEngineHandle")public class MsgEngineHandle extends EngineBase {@Value("${wx.config.originalid:你的Err默認(rèn)值}")private String originalId;@Overridepublic String process(BehaviorMatter request) throws Exception {LogicFilter router = super.router(request);if (null == router) return null;String resultStr = router.filter(request);if (StringUtils.isBlank(resultStr)) return "";//反饋信息[文本],暫時只有文本后續(xù)按需拓展MessageTextEntity res = new MessageTextEntity();res.setToUserName(request.getOpenId());res.setFromUserName(originalId);res.setCreateTime(String.valueOf(System.currentTimeMillis() / 1000L));res.setMsgType("text");res.setContent(resultStr);return XmlUtil.beanToXml(res);}} infrastructure基礎(chǔ)層
@Repository("userAuthGrantRepository")public class UserAuthGrantRepository implements IUserAuthGrantRepository {@Autowiredprivate UserAuthDao userAuthDao;@Overridepublic void grantAuth(String openId, String token) {UserAuth userAuthReq = new UserAuth();userAuthReq.setOpenId(openId);userAuthReq.setToken(token);userAuthReq.setUuid(openId + "_" + token);userAuthDao.insert(userAuthReq);}@Overridepublic void revokeAuth(String openId) {userAuthDao.delete(openId);}} 3.3 部署1. 工程打包
2. 服務(wù)上線
3. 功能驗證
4. 綜上總結(jié)
|
|
|