微信公衆號:bugstack蟲洞棧 沉澱、分享、成長,專一於原創專題案例,以最易學習編程的方式分享知識,讓本身和他人都能有所收穫。目前已完成的專題有;Netty4.x實戰專題案例、用Java實現JVM、基於JavaAgent的全鏈路監控、手寫RPC框架、架構設計專題案例[Ing]等。css
這個!截至到19年11月我已經工做6年了,從業Java但也折騰過C#、搞PHP也弄過中繼器、IO板卡,彷佛我就是一個很喜歡在技術上折騰的人!與此同時,我也搞了6年的我的小網站,它們的呈現形式多種多樣;有用PHP本身搗鼓的技術站用於分享資料、書籍、軟件等、有用PHPWIND和DISCUZ的搭建的我的論壇、有用emlog和wordpress搭建的我的博客、也有借用於github+hexo/jekyll的能力組裝出的技術博客。但無一例外它們都戰死於征戰的路上了,亡於;org域名不能備案、PHP服務器癱瘓被清空、http鏈接被注入惡意內容、定位不許確常常換模式、缺乏核心優質內容等等。但!老衲的心依然如春(cun),由於喜歡幹一件事,每每來自於幹一了件喜歡的事!html
因此!從19年開始我又繼續寫博客了,註冊了新的域名bugstack.cn,備了案、買了服務器、還喊了新的口號;沉澱、分享、成長,讓本身和他人都能有所收穫!而且將塵封已久的微信公衆號找回;結構上調整、內容上佈局、粉絲上求關注。在這期間遇到了更大的牛;小灰、王二哥、純潔的微笑還有鬆哥等一羣夥伴,從他們那學到不少知識,真的很是感謝!前端
那麼!此次的產品功能總結一句話就是;將基於Github+Jekyll搭建的靜態博客與我並未開發過的微信公衆號功能打通,經過在文章短口令碼加鎖引導用戶到公衆號內回覆密碼可解鎖內容,以此來得到粉絲關注,固然若是取消關注了則文章繼續鎖定。java
在多說一句,我理解的產品;實際上是使用研發技術力搭建出能夠用於承載接收用戶在各類設備上所生產的行爲數據的一種產品化服務。因此有些產品在作減法,同時也有爲豐富的功能作加法,但究其一點咱們其實都是在爲接收有價值數據服務的。興衰存亡,皆在覈心數據沉澱與運做上!mysql
爲了使博客粉絲主動關注微信公衆號,咱們在用戶初次瀏覽文章時增長權限驗證,給每個用戶都生成一個惟一碼引導用戶在公衆號內回覆解鎖文章,以此來與微信openid對應。當用戶取消關注時則進行刪除openid或標記狀態,使得用戶沒法繼續瀏覽文章。其實爲了更好的體驗,我參照了大牛的方式內容60%的區域可見,其他內容漸進遮擋,蒙朧朧的感受還挺美。總體流程圖以下; git
爲了實現本產品功能,我準備了;github
前端主要負責針對發佈時設置了look: need的文章,在用戶瀏覽文章檢查是否有權限查看所有內容,當用戶沒有權限時隱藏文章60%內容,並經過頁面結尾提醒用戶在公衆號內回覆口令解鎖文章。web
<article class="post container need lock" itemscope="" itemtype="http://schema.org/BlogPosting" style="height: 4400px;">
<div class="row">
<div class="col-md-9 markdown-body">
<h2 id="前言介紹">前言介紹</h2>
<p>爲何會有路由層?由於在微服務架構設計中,每每並不會直接將服務暴漏給調用端,而是經過調用路由層進行業務隔離,以達到不一樣的業務調用對應的服務模塊。</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();
}
複製代碼
// 查詢後端的結果
var _detect = function() {
console.info(token);
$.ajax({
url : 'https://bugstack.cn/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();
}
})
}
// 定時任務
_detect();
setInterval(function() {
_detect();
}, 5000);
複製代碼
itstack-ark-wx & 領域驅動設計方式設計ajax
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
複製代碼
itstack-ark-wx & 建表語句redis
CREATE TABLE
user_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
複製代碼
講解部分重點代碼塊,完整代碼下載關注公衆號;bugstack蟲洞棧 & 回覆:itstack-ark-wx
interfaces接口層
WxPortalController.java & 接收微信公衆號驗籤與行爲信息通知
微信公衆號都是經過服務提供方的一個接口的get/post請求來執行操做的{這樣設計真sao但真香}
get接口主要是驗證簽名
post接口會收到;關注、取消關注、用戶的回覆信息
/** * 微信公衆號:bugstack蟲洞棧 * 純潔版博客:https://bugstack.cn * 沉澱、分享、成長,讓本身和他人都能有所收穫! * Create by 付政委 on @2019 */
@RestController
@RequestMapping("/wx/portal/{appid}")
public class WxPortalController {
private Logger logger = LoggerFactory.getLogger(WxPortalController.class);
@Autowired
private WxValidateService wxValidateService;
@Autowired
private WxReceiveService wxReceiveService;
/** * 處理微信服務器發來的get請求,進行簽名的驗證 * <p> * appid 微信端AppID * signature 微信端發來的簽名 * timestamp 微信端發來的時間戳 * nonce 微信端發來的隨機字符串 * echostr 微信端發來的驗證字符串 */
@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("請求參數非法,請覈實!");
}
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;
}
}
/** * 此處是處理微信服務器的消息轉發的 */
@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 "";
}
}
}
複製代碼
BlogController.java & 博客驗權接口,用於判斷文章是否解鎖
/** * 微信公衆號:bugstack蟲洞棧 * 純潔版博客:https://bugstack.cn * 沉澱、分享、成長,讓本身和他人都能有所收穫! * Create by 付政委 on @2019 */
@CrossOrigin("https://bugstack.cn")
@RestController
@RequestMapping("/api")
public class BlogController {
private Logger logger = LoggerFactory.getLogger(BlogController.class);
@Autowired
private UserLockAuthService userLockAuthService;
@GetMapping(value = "check", produces = "application/json;charset=utf-8")
public String check(@RequestParam String token) {
try {
logger.info("校驗博客瀏覽用戶受權狀態{}開始", token);
boolean status = userLockAuthService.checkAuth(token);
logger.info("校驗博客瀏覽用戶受權狀態{}完成", token, status);
return status ? "success" : "refuse";
} catch (Exception e) {
logger.error("校驗博客瀏覽用戶受權狀態{}失敗", token, e);
return "refuse";
}
}
}
複製代碼
application應用層
UserLockAuthService.java & 定義後由領域層實現
public interface UserLockAuthService {
boolean checkAuth(String token);
}
複製代碼
domain領域層
logic/LogicFilter.java & 定義邏輯模型,impl中有對應的一組的實現類
public interface LogicFilter {
String filter(BehaviorMatter request);
}
複製代碼
logic/impl/SubscribeFilter.java & 其中一個實現,關注時行爲處理
@Service("subscribe")
public class SubscribeFilter implements LogicFilter {
private final String content = "您好!\n" +
"\n" +
"很是感謝您關注,微信公衆號:bugstack蟲洞棧 | 也期待您分享給更多小夥伴!\n" +
"\n" +
"bugstack蟲洞棧,專一於原創技術專題案例,以最易學習編程開發的方式分享技術知識,讓萌新、小白、大牛都能有所收穫。目前已經完成的專題有;《Netty4.x從入門到實戰》、《手寫RPC框架》、《用Java實現JVM》、《基於JavaAgent的全鏈路監控》、《DDD專題案例》,其餘更多專題還在排兵佈陣中。\n" +
"\n" +
"獲取專題案例源碼回覆;netty案例、rpc案例、用Java實現jvm源碼、基於JavaAgent的全鏈路監控案例、DDD落地。\n" +
"\n" +
"聯繫做者:付政委 | monkeycode";
@Override
public String filter(BehaviorMatter request) {
return content;
}
}
複製代碼
engine/impl/MsgEngineHandle.java & 引擎路由調用
@Service("msgEngineHandle")
public class MsgEngineHandle extends EngineBase {
@Value("${wx.config.originalid:你的Err默認值}")
private String originalId;
@Override
public 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 "";
//反饋信息[文本],暫時只有文本後續按需拓展
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基礎層
repository/UserAuthGrantRepository.java & 數據庫實現
@Repository("userAuthGrantRepository")
public class UserAuthGrantRepository implements IUserAuthGrantRepository {
@Autowired
private UserAuthDao userAuthDao;
@Override
public void grantAuth(String openId, String token) {
UserAuth userAuthReq = new UserAuth();
userAuthReq.setOpenId(openId);
userAuthReq.setToken(token);
userAuthReq.setUuid(openId + "_" + token);
userAuthDao.insert(userAuthReq);
}
@Override
public void revokeAuth(String openId) {
userAuthDao.delete(openId);
}
}
複製代碼
springboot配置打包部署成可運行war包
pom.xml
<packaging>war</packaging>
複製代碼
WxApplication.java
@SpringBootApplication
public class WxApplication extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(WxApplication.class);
}
public static void main(String[] args) {
SpringApplication.run(WxApplication.class, args);
}
}
複製代碼
修改配置application.yml,數據庫配置、微信配置
server:
port: 80
spring:
# datasource:
# username: root
# password: 123456
# url: jdbc:mysql://localhost:3306/itstack-ark-wx?useUnicode=true&characterEncoding=utf8
# driver-class-name: com.mysql.jdbc.Driver
mybatis:
mapper-locations: classpath:/mybatis/mapper/*.xml
config-location: classpath:/mybatis/config/mybatis-config.xml
# 微信公衆號配置信息
# originalid:原始ID
# appid:我的AppID
# token:開通接口服務自定義設置
wx:
config:
originalid: xxxxxxx
appid: xxxxxxx
token: xxxxxxx
複製代碼
[root@instance-39394m67 bin]# ./startup.sh
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.1.2.RELEASE)
2019-11-23 18:10:57.131 INFO 22052 --- [ost-startStop-1] org.itstack.ark.wx.WxApplication : Starting WxApplication v1.0.0-SNAPSHOT on instance-39394m67 with PID 22052 (/usr/local/java/apache-tomcat-8.5.37/webapps/itstack-ark-wx/WEB-INF/classes started by root in /usr/local/java/apache-tomcat-8.5.37/bin)
2019-11-23 18:10:57.158 INFO 22052 --- [ost-startStop-1] org.itstack.ark.wx.WxApplication : No active profile set, falling back to default profiles: default
2019-11-23 18:10:59.137 INFO 22052 --- [ost-startStop-1] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 1890 ms
2019-11-23 18:11:01.050 INFO 22052 --- [ost-startStop-1] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor'
2019-11-23 18:11:01.336 WARN 22052 --- [ost-startStop-1] ion$DefaultTemplateResolverConfiguration : Cannot find template location: classpath:/templates/ (please add some templates or check your Thymeleaf configuration)
2019-11-23 18:11:01.677 INFO 22052 --- [ost-startStop-1] org.itstack.ark.wx.WxApplication : Started WxApplication in 5.873 seconds (JVM running for 10.311)
23-Nov-2019 18:11:01.718 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployWAR Deployment of web application archive [/usr/local/java/apache-tomcat-8.5.37/webapps/itstack-ark-wx.war] has finished in [9,226] ms
23-Nov-2019 18:11:01.720 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Deploying web application directory [/usr/local/java/apache-tomcat-8.5.37/webapps/ROOT]
23-Nov-2019 18:11:01.746 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Deployment of web application directory [/usr/local/java/apache-tomcat-8.5.37/webapps/ROOT] has finished in [26] ms
23-Nov-2019 18:11:01.753 INFO [main] org.apache.coyote.AbstractProtocol.start Starting ProtocolHandler ["http-nio-80"]
23-Nov-2019 18:11:01.764 INFO [main] org.apache.coyote.AbstractProtocol.start Starting ProtocolHandler ["ajp-nio-8009"]
23-Nov-2019 18:11:01.766 INFO [main] org.apache.catalina.startup.Catalina.start Server startup in 9326 ms
2019-11-23 18:11:13.039 INFO 22052 --- [p-nio-80-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2019-11-23 18:11:13.059 INFO 22052 --- [p-nio-80-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 20 ms
2019-11-23 18:11:13.172 INFO 22052 --- [p-nio-80-exec-1] o.i.ark.wx.interfaces.BlogController : 校驗博客瀏覽用戶受權狀態UDHIUS開始
複製代碼
引導提示;
文章解鎖;