Simple and easy nail group message push Assistant

We often encounter needs to send a message to the nail base, so I put this simple tool has become functional abstraction: a group message push nail assistant, and it is open source. Project Address: github.com/all4you/wal...

Wali

Wali is a lightweight nail group messaging assistant, you only need to configure a template for a message sent by the Wali (supports multiple addresses, and can be modified dynamically at runtime), you can quickly realize the nail group messages the transmission function.

characteristic

  • Template management : group message template management, currently supports TEXT, MARKDOWN, LINK three types of group messages
  • Multigroup match : same template support groups simultaneously sent to multiple nails, and supports conditional expressions routing group
  • JWT certificate management : the management of user credentials by JWT
  • Open Interface : External exposure REST interface for the user to trigger the group message
  • Extension point : for interface design, the user can implement various self extensibility points

Quick Start

  • First, we need to deploy the project.
  • After the project deployment is successful, then create an account and templates.
  • After the account and templates are created, you can call the interface.

Deployment Project

Download Project

git clone http://github.com/all4you/walle
复制代码

Packaging Project

cd walle
mvn clean package
复制代码

Skip test when packaging:

mvn clean --DskipTests package
复制代码

Lay package in this directory:

./walle-core/target/walle-core-0.0.1.jar
复制代码

Startup project

After packed the command line can be started directly:

java -jar ./walle-core/target/walle-core-0.0.1.jar
复制代码

Or you can start the project directly through the mvn command:

cd walle-core
mvn spring-boot:run
复制代码

Database Resources

Create the appropriate database, and create a good table structure.

Specific script: walle / walle-core / src / resources / sql / tables.sql

Note that the database account password to create a consistent and application.properties file.

Front-end resources

The item corresponding to the front page by vue achieved, corresponding to the front end projects walle-web

After modifying the page you will need a copy of the latest package of resources to the resource directory walle

1. Packaging

# 构建生产环境
npm run build:prod
复制代码

2. Resource copy

Packaged resources in the dist / directory, all files in this directory are copied to walle resource directory:

rm -rf ~/walle/walle-core/src/main/resources/static/*
cp -R dist/* ~/walle/walle-core/src/main/resources/static/
复制代码

Create an account and templates

Create an account

After the project up and running, open http: // localhost: 7001

The system will enter the login page, as shown below:

login.jpg

Item no built-in account, we click on the "Register Now" to enter the account registration page:

register.jpg

After successful registration, automatically jump back to the login page, enter the registration after a good account password, then click Login to enter the home.

Create a template

After a successful login, go to our homepage, then click on the left side of the "template" - "group message" to enter the message template list group.

Then click on the top right of the "Add" button, enter a template to create a page, as shown below:

group_board_create1.jpg

Currently supports a total of three types of group messages: Text, Markdown, Link.

We send group messages Markdown type, for example, create a message template, the specific contents are as follows:

group_board_create2.jpg

Because it is a type Markdown, it is a Markdown text editor to edit must be Markdown syntax.

@ 1381338xxxx can add phone numbers in the text to make the robot group in Ait specific personnel. Text and title support velocity syntax, you can add variables.

At least one receiving a target template, the target may be provided a plurality of receiving, a receiving target has four attributes:

Attributes meaning Mandatory
Robot Name Name used to identify this group of robots Yes
accessToken The group accessToken robot, the robot can get after creating the group Yes
Rule Condition A condition expression evaluates to true when the message will be sent no
Secret Endorsement of the secret key group settings robot no

Which must accessToken is the name of the property and the other based on security considerations, there are three types of security settings ( see details ):

  • Custom Keywords
  • Endorsement
  • IP address (segment)

So now we create a group of robots, it is necessary to set up a safe setting, where I chose the second endorsement of the way, you only need to key settings when creating robots fill in the Secret field inside enough.

Send a message

A. Single reception target

Select a template and click send a message to enter the page message is sent, as shown below:

send_msg_1.jpg

When sending a message that can carry data, text template, heading into the final result will be rendered by Velocity.

Conditional expression will compute the result with aviator, the result is true, will actually perform the operation sent.

After good data editing, click send, nothing else should be sent successfully, as shown below:

send_msg_success_1.jpg

Then you will receive a message of the staple group, as shown below:

send_msg_success_2.jpg

If we carry only one level of data fields that will receive this message:

send_msg_success_3.jpg

Message value and time sources are empty, the value is the position of ${location}the string, which is variable according to the template provided there is an exclamation mark is determined.

If you add an exclamation mark: $!{deviceType}When this variable is empty, rendering the result will be an empty string, otherwise it will show the variables directly.

More than two receiving target

We can at the same time a message is sent to a number of different groups nails, as long as a plurality of receiving target in the template like, not repeat them here.

III. Application Conditions matches

Each receiving a target conditional expressions may be provided, the values ​​from the previous transmission of the conditional expression, a result message is sent only true.

For example the following two reception target set, the conditional expression is set to [level == 'high'], then only when there is data carried in the level variable, and the value is equal to high, message transmission will be performed.

PS: calculation of the conditional expression based aviator

As shown below, when the data carried is empty, the message sent:

send_msg_error_1.jpg

The use of the conditional expression, we can do many things, such as:

  • The device is offline different levels of alert, warning distributed to different groups, so that different people to deal
  • The expression used as a switch, such as setting to the expression: [false] or [2 == 1] that a value equivalent to the target message is not sent to the receiver

Interface calls

Page is a template management, and ultimately send a message or to be operated by the interface.

Only two steps to send a message through the interface:

1. introduced walle-api module:

<dependency>
    <groupId>com.alibaba.walle</groupId>
    <artifactId>walle-api</artifactId>
    <version>0.0.1</version>
</dependency>
复制代码

2. Create WalleClient and send a message:

WalleConfig config = WalleConfig.builder()
        .endPoint("http://127.0.0.1:7001")
        .accessKey("m74hscNSZPVWo3tK")
        .secretKey("QsfigP8ibVhgFv5QmcPHkwsV")
        .build();
// 创建 WalleClient
WalleClient walleClient = new WalleHttpClient(config);
// 设置请求参数
GroupMessageDTO messageDTO = new GroupMessageDTO();
// 模板编号
messageDTO.setBoardCode("device_offline");
// 携带的数据
JSONObject data = new JSONObject();
data.put("level", "high");
messageDTO.setData(data);
// 发送请求
BaseResult result = walleClient.sendGroupMessage(messageDTO);
System.out.println(result);
复制代码

Which accessKey and secretKey in the center of [individual] - [Security Settings] in view.

User credentials: JWT

Generation and checking user credentials is achieved by JWT.

The WEB the Token JSON (the JWT) , is based on JSON for tokens (token) declared certain claims in the network.

JWT usually consists of three parts:

  • Header information (header)
  • Message body (payload)
  • Signed (signature)

Header specifies the signature algorithm used by the JWT:

header = '{"alg":"HS256","typ":"JWT"}'
复制代码

HS256 He expressed using HMAC-SHA256 to generate a signature.

Message body contains a JWT's intentions:

payload = '{"loggedInAs":"admin","iat":1422779638}' // iat表示令牌生成的时间
复制代码

The signature is based on header and payload encryption available, specific principles are as follows:

JWT data comprises three parts: abc a is a header (header portion), data is stored in a Map called headerClaims b is payload (load section), the data stored can be defined in a call payloadClaims payloadClaims Map of: + - Claim standard Claim, JWT retained, can be similar: withIssuedAt way to declare + - + Claim public - private Claim, may declare Claim custom, by means of: addClaim (key, value) to c is declared signature (visa section),, payload, header obtained from the secret encrypted by the pseudo code is as follows:

String base64Header = base64(header);
String base64Payload = base64(payload);
Algorithm algorithm = Algorithm(secret);
String signature = algorithm.sign(base64Header, base64Payload);
String base64Signature = base64(signature);
复制代码

The final token consists of three parts:

String jwtToken = String.format("%s.%s.%s", base64Header, base64Payload, base64Signature);
复制代码

JWT token management using java-jwt library: github.com/auth0/java-...

Generated JWT

After the user logs in successfully, create a JWT for the user, and the user id, login time and other non-sensitive information is stored in the JWT:

public String newToken(UserDO user, Date loginDate) {
    String tokenId = IdUtil.objectId();
    // token中保存了部分非敏感信息
    return JWT.create()
            // 设置创建时间
            .withIssuedAt(DateUtil.date())
            // 设置过期时间,1小时
            .withExpiresAt(expireDate())
            // 将部分信息保存到 PayloadClaim 的私有Claim中
            .withClaim("userId", user.getUserId())
            .withClaim("tokenId", tokenId)
            .withClaim("account", user.getAccount())
            .withClaim("gmtCreate", DateUtil.formatDateTime(user.getGmtCreate()))
            .withClaim("accessKey", user.getAccessKey())
            .withClaim("lastLogin", DateUtil.formatDateTime(loginDate))
            // 以 JWT_SECRET 作为 token 的密钥对jwt中的数据进行加密
            .sign(Algorithm.HMAC256(this.secret()));
}
复制代码

After generating jwt, the header of the HttpServletResponse jwt written:

// 3.生成token
String token = tokenFactory.newToken(userDO, DateUtil.date());
/*
 * 为了防止跨域请求(CORS),浏览器只能获得几个默认的响应头:
 * <ul>
 *  <li> Cache-Control </li>
 *  <li> Content-Language </li>
 *  <li> Content-Type </li>
 *  <li> Expires </li>
 *  <li> Last-Modified </li>
 *  <li> Pragma </li>
 * </ul>
 * 如果让浏览器能获取其他响应头,需要在响应头中指定需要暴露的响应头
 */
response.addHeader("Access-Control-Expose-Headers", "walle-token");
response.addHeader("walle-token", token);
复制代码

Jwt then remove it from the front end of the first response, stored in the Cookie, and all subsequent requests, the jwt added to the Request Header.

Check JWT

In order to determine the legality of the server for each interface, the request needs to be verified in the header token, data specific to the operation after the check is passed.

This part of the verify operation can be achieved by Spring interceptor, specifically the check section as follows:

public boolean validToken(String token) {
    JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(this.secret())).build();
    try {
        jwtVerifier.verify(token);
    } catch (JWTVerificationException e) {
        GenericLogUtil.invokeError(log, "validToken", StrFormatter.format("token={}", token), e);
        return false;
    }
    return true;
}
复制代码

But we can only be judged by the secret key JWT is legitimate, do not take the initiative to make a JWT failure, for example, we created a JWT, and set one hour valid within one hour of this, are valid , even if the server restarted.

The version number of record JWT

Since we can not set aside a server JWT issued, because the validation rules JWT itself is only a validity period.

In order to ensure JWT server can be actively generated invalid, for example, log off the user, and then change the password, we need to take the initiative before the enactment of the user JWT void.

To achieve this goal, we can record a version number issued by JWT, the version number can be deleted when you need to set aside a JWT, will be associated.

In the end the service with a Map to record the number of each version of JWT:

key ===> userId#tokenId
val ===> version
复制代码

When you need to set aside a JWT, the version number of deleted records Map.

Detailed information is available com.ngnis.walle.core.auth.TokenFactory

PS: This method is only for stand-alone deployment, if the server requires a distributed deployment, you need to JWT version number stored in Redis or other public storage center.

Extension point: ** Center

Currently Wali is an interface-oriented design, the core of the account, templates, message management defines a standard interface and provides a default implementation class.

It may be determined according to the actual needs of the user using the default implementation class or customize their own implementation class, and injected into Spring container.

  • AccountCenter : Center account, by @EnableAccountCenterdefault implementation class is introduced
  • GroupBoardCenter : Template Center, by @EnableGroupBoardCenterdefault implementation class is introduced
  • MessageCenter : message center, via @EnableMessageCenterthe default implementation class introduced

Or also by @EnableWallethe implementation class introducing all of the default.

I welcome the attention of the public numbers: by code, focusing on original Share

I welcome the attention of the public numbers: by code, focusing on original Share

Guess you like

Origin juejin.im/post/5e8149c451882573883bb25f