shiro
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.6.0</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.6.0</version>
</dependency>
package com.github.shop.config;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.cache.MemoryConstrainedCacheManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.Map;
@Configuration
public class ShiroConfig {
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
Map<String, String> pattern = new HashMap<>();
pattern.put("/api/v1/code", "anon");
pattern.put("/api/v1/login", "anon");
shiroFilterFactoryBean.setFilterChainDefinitionMap(pattern);
return shiroFilterFactoryBean;
}
@Bean
public SecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setCacheManager(new MemoryConstrainedCacheManager());
securityManager.setSessionManager(new DefaultWebSecurityManager());
SecurityUtils.setSecurityManager(securityManager);
return securityManager;
}
}
AuthController.java
package com.github.shop.controller;
import com.github.shop.service.AuthService;
import com.github.shop.service.TelVerificationService;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletResponse;
@RestController
@RequestMapping("/api/v1")
public class AuthController {
private final AuthService authService;
private final TelVerificationService telVerificationService;
public AuthController(AuthService authService, TelVerificationService telVerificationService) {
this.authService = authService;
this.telVerificationService = telVerificationService;
}
/**
* @api {post} /code 请求验证码
* @apiName GetCode
* @apiGroup 登录与授权
*
* @apiHeader {String} Accept application/json
* @apiHeader {String} Content-Type application/json
*
* @apiParam {String} tel 手机号码
* @apiParamExample {json} Request-Example:
* {
* "tel":"13912345678"
* }
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* @apiErrorExample Error-Response:
* HTTP/1.1 400 Bad Request
* {
* "message":"Bad Request"
* }
*/
/**
* @param telAndCode 电话和验证码
* @param response HTTP response
*/
@PostMapping("/code")
public void code(@RequestBody TelAndCode telAndCode, HttpServletResponse response){
if (telVerificationService.verifyTelParameter(telAndCode)) {
authService.sendVerificationCode(telAndCode.getTel());
} else {
response.setStatus(HttpStatus.BAD_REQUEST.value());
}
}
@PostMapping("/login")
public void login(){
}
public static class TelAndCode {
private String tel;
private String code;
public TelAndCode(String tel, String code) {
this.tel = tel;
this.code = code;
}
public String getTel() {
return tel;
}
public void setTel(String tel) {
this.tel = tel;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
}
}
文档是使用的apiDoc
/**
* @api {post} /code 请求验证码
* @apiName GetCode
* @apiGroup 登录与授权
*
* @apiHeader {String} Accept application/json
* @apiHeader {String} Content-Type application/json
*
* @apiParam {String} tel 手机号码
* @apiParamExample {json} Request-Example:
* {
* "tel":"13912345678"
* }
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* @apiErrorExample Error-Response:
* HTTP/1.1 400 Bad Request
* {
* "message":"Bad Request"
* }
*/
/**
* @param telAndCode 电话和验证码
* @param response HTTP response
*/
主要设置:
npm install apidoc -g
mac一般会因为权限报错 加一个sudo就可以了
下载后添加apidoc.json
{
"name": "商城项目API文档",
"version": "0.1.0",
"description": "A basic apiDoc example",
"url": "http://localhost:8080/api/v1"
}
然后使用命令
apidoc -i myapp/ -o apidoc/ -t mytemplate/
示范命令:
apidoc -i /Users/zuojiabin/dev/shop -o /Users/zuojiabin/dev/shop/apidoc/
然后就可以创建好看的api文档了
AuthService.java
package com.github.shop.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class AuthService {
private final UserService userService;
private final VerificationCodeCheckService verificationCodeCheckService;
private final SmsCodeService smsCodeService;
@Autowired
public AuthService(UserService userService,
VerificationCodeCheckService verificationCodeCheckService,
SmsCodeService smsCodeService) {
this.userService = userService;
this.verificationCodeCheckService = verificationCodeCheckService;
this.smsCodeService = smsCodeService;
}
public void sendVerificationCode(String tel) {
userService.createUserIfNotExists(tel);
String correctCode = smsCodeService.sendSmsCode(tel);
verificationCodeCheckService.addCode(tel,correctCode);
}
}
UserService.java
package com.github.shop.service;
import com.github.shop.dao.UserDao;
import com.github.shop.generate.User;
import org.apache.ibatis.exceptions.PersistenceException;
import org.springframework.stereotype.Service;
import java.util.Date;
@Service
public class UserService {
private final UserDao userDao;
public UserService(UserDao userDao) {
this.userDao = userDao;
}
public User createUserIfNotExists(String tel) {
User user = new User();
user.setTel(tel);
user.setCreatedAt(new Date());
user.setUpdatedAt(new Date());
try {
userDao.insertUser(user);
} catch (PersistenceException e) {
return userDao.getUserByTel(tel);
}
return user;
}
}
SmsCodeService.java
package com.github.shop.service;
import org.springframework.stereotype.Service;
@Service
public interface SmsCodeService {
/**
* 向一个指定手机号码发送验证码,返回正确验证码
* @param tel 目标手机号
* @return 正确的验证码
*/
String sendSmsCode(String tel);
}
MockSmsCodeService.java
package com.github.shop.service;
import org.springframework.stereotype.Service;
@Service
public class MockSmsCodeService implements SmsCodeService{
@Override
public String sendSmsCode(String tel) {
return "0000";
}
}
VerificationCodeCheckService.java
package com.github.shop.service;
import org.springframework.stereotype.Service;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Service
public class VerificationCodeCheckService {
private Map<String, String> telNumberToCorrectCode = new ConcurrentHashMap<>();
public void addCode(String tel, String correctCode) {
telNumberToCorrectCode.put(tel, correctCode);
}
}
UserDao.java
package com.github.shop.service;
import com.github.shop.dao.UserDao;
import com.github.shop.generate.User;
import org.apache.ibatis.exceptions.PersistenceException;
import org.springframework.stereotype.Service;
import java.util.Date;
@Service
public class UserService {
private final UserDao userDao;
public UserService(UserDao userDao) {
this.userDao = userDao;
}
public User createUserIfNotExists(String tel) {
User user = new User();
user.setTel(tel);
user.setCreatedAt(new Date());
user.setUpdatedAt(new Date());
try {
userDao.insertUser(user);
} catch (PersistenceException e) {
return userDao.getUserByTel(tel);
}
return user;
}
}
TelVerificationService.java
package com.github.shop.service;
import com.github.shop.controller.AuthController;
import org.springframework.stereotype.Service;
import java.util.regex.Pattern;
@Service
public class TelVerificationService {
private static Pattern TEL_PATTERN = Pattern.compile("1\\d{10}");
/**
* 验证电话的合法性 必须是以1开头的11位数字的中国大陆电话
* @param param 输入的参数
* @return true->电话合法 false->电话不合法
*/
public boolean verifyTelParameter(AuthController.TelAndCode param) {
if (param == null) {
return false;
} else if (param.getTel() == null) {
return false;
} else {
return TEL_PATTERN.matcher(param.getTel()).find();
}
}
}
TelVerificationServiceTest.java
package com.github.shop.service;
import com.github.shop.controller.AuthController;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
public class TelVerificationServiceTest {
public static AuthController.TelAndCode VALID_PARAMETER = new AuthController.TelAndCode("12345678901", null);
public static AuthController.TelAndCode EMPTY_TEL = new AuthController.TelAndCode(null, null);
@Test
public void returnTrueIfValid() {
Assertions.assertTrue(new TelVerificationService().verifyTelParameter(VALID_PARAMETER));
}
@Test
public void returnFalseIfNoTel() {
Assertions.assertFalse(new TelVerificationService().verifyTelParameter(EMPTY_TEL));
Assertions.assertFalse(new TelVerificationService().verifyTelParameter(null));
}
}
CodeIntegrationTest.java
package com.github.shop;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.kevinsawicki.http.HttpRequest;
import com.github.shop.service.TelVerificationServiceTest;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.core.env.Environment;
import org.springframework.http.MediaType;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import static java.net.HttpURLConnection.HTTP_BAD_REQUEST;
@ExtendWith(SpringExtension.class)
@SpringBootTest(classes = ShopApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@TestPropertySource(locations = "classpath:application.yml")
public class CodeIntegrationTest {
@Autowired
Environment environment;
private ObjectMapper objectMapper = new ObjectMapper();
@Test
public void returnHttpBadRequestWhenParameterIsCorrect() throws JsonProcessingException {
int responseCode = HttpRequest.post(getUrl("/api/v1/code"))
.contentType(MediaType.APPLICATION_JSON_VALUE)
.accept(MediaType.APPLICATION_JSON_VALUE)
.send(objectMapper.writeValueAsString(TelVerificationServiceTest.EMPTY_TEL))
.code();
Assertions.assertEquals(HTTP_BAD_REQUEST, responseCode);
}
private String getUrl(String apiName) {
// 获取集成测试的端口号
return "http://localhost:" + environment.getProperty("local.server.port") + apiName;
}
}
mvn verify
配置checkstyle 添加依赖
<plugin>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<configLocation>${basedir}/checkstyle.xml</configLocation>
<includeTestSourceDirectory>true</includeTestSourceDirectory>
<enableRulesSummary>false</enableRulesSummary>
<excludes>**/generate/**</excludes>
</configuration>
<executions>
<execution>
<id>compile</id>
<phase>compile</phase>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>com.puppycrawl.tools</groupId>
<artifactId>checkstyle</artifactId>
<version>8.29</version>
</dependency>
</dependencies>
</plugin>
在根目录下创建checkstyle.xml
<?xml version="1.0"?>
<!DOCTYPE module PUBLIC
"-//Puppy Crawl//DTD Check Configuration 1.1//EN"
"http://www.puppycrawl.com/dtds/configuration_1_1.dtd">
<module name="Checker">
<property name="localeLanguage" value="en"/>
<module name="NewlineAtEndOfFile">
<property name="lineSeparator" value="lf"/>
</module>
<module name="FileTabCharacter">
<property name="fileExtensions" value="java,xml"/>
</module>
<module name="RegexpSingleline">
<!-- \s matches whitespace character, $ matches end of line. -->
<property name="format" value="\s+$"/>
<property name="message" value="Line has trailing spaces."/>
</module>
<module name="TreeWalker">
<module name="IllegalImport"/>
<module name="RedundantImport"/>
<module name="UnusedImports"/>
<module name="NeedBraces"/>
<module name="JavadocMethod">
<property name="scope" value="public"/>
</module>
<module name="ModifierOrder"/>
<module name="RedundantModifier"/>
<module name="UpperEll"/>
<module name="LeftCurly"/>
<module name="NeedBraces"/>
<module name="RightCurly"/>
<module name="GenericWhitespace"/>
<module name="WhitespaceAfter"/>
<module name="NoWhitespaceBefore"/>
</module>
</module>
mvn checkstyle:check
改掉checkstyle的问题 看到build success也是很爽的。至少是遵守一定的代码规范的约定啦。
spotbugs jacoco
<plugin>
<groupId>com.github.spotbugs</groupId>
<artifactId>spotbugs-maven-plugin</artifactId>
<version>3.1.12</version>
<configuration>
<excludeFilterFile>ignore.xml</excludeFilterFile>
</configuration>
<dependencies>
<dependency>
<groupId>com.github.spotbugs</groupId>
<artifactId>spotbugs</artifactId>
<version>4.0.0-beta3</version>
</dependency>
</dependencies>
<executions>
<execution>
<id>spotbugs</id>
<phase>verify</phase>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.5</version>
<executions>
<execution>
<id>default-prepare-agent</id>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>default-report</id>
<phase>verify</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
<execution>
<id>default-check</id>
<goals>
<goal>check</goal>
</goals>
<configuration>
<rules>
<rule>
<element>BUNDLE</element>
<limits>
<limit>
<counter>COMPLEXITY</counter>
<value>COVEREDRATIO</value>
<minimum>0.40</minimum>
</limit>
</limits>
</rule>
</rules>
</configuration>
</execution>
</executions>
</plugin>
<?xml version="1.0" encoding="UTF-8"?>
<FindBugsFilter>
<Match>
<Class name="~.*\.generate\..*"/>
</Match>
</FindBugsFilter>
过滤掉spotbugs排查mybatis-generator生成的代码
因为都绑定在maven verify上 所以运行mvn verify 就可以运行jacoco checkstyle spotbugs这三个插件
target/site/jacoco/index.html在浏览器中打开这个html就可以看到jacoco的测试率报告。
就知道后面还需要做什么了