pom.xml
<?xml version="1.0" encoding="UTF-8"?>
4.0.0
org.springframework.boot
spring-boot-starter-parent
2.1.4.RELEASE
top.lrshuai
google-check
0.0.1-SNAPSHOT
SpringBoot-GoogleCheck
google check<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<swagger2.version>2.7.0</swagger2.version>
</properties>
<dependencies>
<!-- spring boot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.6</version>
</dependency>
<!-- redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<!-- json -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.56</version>
</dependency>
<!-- swagger2 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>${swagger2.version}</version>
<exclusions>
<exclusion>
<artifactId>mapstruct</artifactId>
<groupId>org.mapstruct</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>${swagger2.version}</version>
</dependency>
<!-- 加密工具 -->
<dependency>
<groupId>top.lrshuai.encryption</groupId>
<artifactId>encryption-tools</artifactId>
<version>1.0.0</version>
</dependency>
<!--google qrcode -->
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>javase</artifactId>
<version>3.2.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build> annotaion package top.lrshuai.googlecheck.annotation;
import java.lang.annotation.*;
/**
• 是否需要登录注解
•
*/
@Documented
@Inherited
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface NeedLogin {
// 是否有效,如果注解在类上,又想要某个方法上不生效,可用这个配置
boolean isValid() default true;
//登录注解
boolean login() default true;
//google验证注解
boolean google() default false;
}
basecontroller
package top.lrshuai.googlecheck.base;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import top.lrshuai.googlecheck.common.ApiException;
import top.lrshuai.googlecheck.common.ApiResultEnum;
import top.lrshuai.googlecheck.common.CacheEnum;
import top.lrshuai.googlecheck.entity.User;
import top.lrshuai.googlecheck.utils.Tools;import javax.servlet.http.HttpServletRequest;
/**
• 基类
*/
public class BaseController {
@Autowired
private RedisTemplate<String,Object> redisTemplate;
public HttpServletRequest getRequest() {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
.getRequest();
return request;
}
/**• 从token 中获取用户信息
• @return
*/
protected User getUser(){
String tokenKey = Tools.getTokenKey(this.getRequest(), CacheEnum.LOGIN);
User user = (User) redisTemplate.opsForValue().get(tokenKey);
if(user == null) throw new ApiException(ApiResultEnum.AUTH_LGOIN_NOT_VALID);
return user;
}
}
common
package top.lrshuai.googlecheck.common;import lombok.Data;
/**
• 自定义的api异常
• @author rstyro
•
*/
@Data
public class ApiException extends RuntimeException{
private static final long serialVersionUID = 1L;
private String status;
private String message;
private Object data;
private Exception exception;
public ApiException() {
super();
}
public ApiException(String status, String message, Object data, Exception exception) {
this.status = status;
this.message = message;
this.data = data;
this.exception = exception;
}
public ApiException(ApiResultEnum apiResultEnum) {
this(apiResultEnum.getStatus(),apiResultEnum.getMessage(),null,null);
}
public ApiException(ApiResultEnum apiResultEnum, Object data) {
this(apiResultEnum.getStatus(),apiResultEnum.getMessage(),data,null);
}
public ApiException(ApiResultEnum apiResultEnum, Object data, Exception exception) {
this(apiResultEnum.getStatus(),apiResultEnum.getMessage(),data,exception);
}}
package top.lrshuai.googlecheck.common;public enum ApiResultEnum {
SUCCESS(“200”,“ok”),
FAILED(“400”,“请求失败”),
ERROR(“500”,“不知名错误”),
ERROR_NULL(“501”,“空指针异常”),
ERROR_CLASS_CAST(“502”,“类型转换异常”),
ERROR_RUNTION(“503”,“运行时异常”),
ERROR_IO(“504”,“上传文件异常”),
ERROR_MOTHODNOTSUPPORT(“505”,“请求方法错误”),AUTH_LGOIN_NOT_VALID("10000","用户未登录,或token过期"),
AUTH_GOOGLE_NOT_FOUND("10001","未Goole校验,无法访问"),
USER_IS_EXIST("10003","用户已存在"),
USER_NOT_EXIST("10004","用户不存在"),
USERNAME_OR_PASSWORD_IS_WRONG("10005","用户名密码错误"),
GOOGLE_CODE_NOT_MATCH("10006","Google验证码不匹配"),
GOOGLE_IS_BIND("10007","Google已绑定,不能重复绑定"),
GOOGLE_NOT_BIND("10008","Google未绑定,请先进行绑定"),
;
private String message;
private String status;
public String getMessage() {
return message;
}
public String getStatus() {
return status;
}
private ApiResultEnum(String status, String message) {
this.message = message;
this.status = status;
}}
package top.lrshuai.googlecheck.common;public enum CacheEnum {
LOGIN,GOOGLE
}package top.lrshuai.googlecheck.common;
public class CacheKey {
/**
* 登录生成的token key
*/
public static final String TOKEN_KEY_LOGIN = “TOKEN_KEY_LOGIN-%s”;/**
* google验证保存的状态 key
*/
public static final String TOKEN_KEY_GOOGLE = "TOKEN_KEY_GOOGLE-%s";
/**
* 注册的用户全部放redis缓存中
*/
public static final String REGISTER_USER = "REGISTER_USER_%s";
public static final String REGISTER_USER_KEY = "REGISTER_USER_*";
public static final String TOKEN_KEY_LOGIN_KEY = "TOKEN_KEY_LOGIN*";}
package top.lrshuai.googlecheck.common;
public class Consts {
/**
* 前端传上来的 token 参数
*/
public static final String TOKEN = “token”;public static final String SUCCESS = "SUCCESS";}
package top.lrshuai.googlecheck.common;
import java.util.HashMap;
public class Result extends HashMap<String, Object> {
private static final long serialVersionUID = 1L;
public Result() {
put("status", 200);
put("message", "ok");
}
public static Result error() {
return error("500", "系统错误,请联系管理员");
}
public static Result error(String msg) {
return error("500", msg);
}
public static Result error(String status, String msg) {
Result r = new Result();
r.put("status", status);
r.put("message", msg);
return r;
}
public static Result error(ApiResultEnum resultEnum) {
Result r = new Result();
r.put("status", resultEnum.getStatus());
r.put("message", resultEnum.getMessage());
return r;
}
public static Result ok(Object data) {
Result r = new Result();
r.put("data",data);
return r;
}
public static Result ok() {
return new Result();
}
@Override
public Result put(String key, Object value) {
super.put(key, value);
return this;
}}
config
package top.lrshuai.googlecheck.config;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import top.lrshuai.googlecheck.common.ApiException;
import top.lrshuai.googlecheck.common.ApiResultEnum;
import top.lrshuai.googlecheck.common.Result;import java.io.IOException;
/**
• 全局异常捕获
• @author rstyro
• @since 2019-03-12
*/
@RestControllerAdvice
public class GlobalExceptionHandler {
private Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
@ExceptionHandler(NullPointerException.class)
public Result NullPointer(NullPointerException ex){
logger.error(ex.getMessage(),ex);
return Result.error(ApiResultEnum.ERROR_NULL);
}
@ExceptionHandler(ClassCastException.class)
public Result ClassCastException(ClassCastException ex){
logger.error(ex.getMessage(),ex);
return Result.error(ApiResultEnum.ERROR_CLASS_CAST);
}
@ExceptionHandler(IOException.class)
public Result IOException(IOException ex){
logger.error(ex.getMessage(),ex);
return Result.error(ApiResultEnum.ERROR_IO);
}
@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
public Result HttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException ex){
logger.error(ex.getMessage(),ex);
return Result.error(ApiResultEnum.ERROR_MOTHODNOTSUPPORT);
}
@ExceptionHandler(ApiException.class)
public Result ApiException(ApiException ex) {
logger.error(ex.getMessage(),ex);
return Result.error(ex.getStatus(),ex.getMessage());
}
@ExceptionHandler(RuntimeException.class)
public Result RuntimeException(RuntimeException ex){
logger.error(ex.getMessage(),ex);
return Result.error(ApiResultEnum.ERROR_RUNTION);
}
@ExceptionHandler(Exception.class)
public Result exception(Exception ex){
logger.error(ex.getMessage(),ex);
return Result.error(ApiResultEnum.ERROR);
}}
package top.lrshuai.googlecheck.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Set;/**
*• @author rstyro
• @time 2017-07-31
•
*/
@Configuration
//@EnableCaching // 开启缓存支持
public class RedisConfig extends CachingConfigurerSupport {
@Resource
private LettuceConnectionFactory lettuceConnectionFactory;@Bean
public KeyGenerator keyGenerator() {
return new KeyGenerator() {
@Override
public Object generate(Object target, Method method, Object... params) {
StringBuffer sb = new StringBuffer();
sb.append(target.getClass().getName());
sb.append(method.getName());
for (Object obj : params) {
sb.append(obj.toString());
}
return sb.toString();
}
};
}
// 缓存管理器
@Bean
public CacheManager cacheManager() {
RedisCacheManager.RedisCacheManagerBuilder builder = RedisCacheManager.RedisCacheManagerBuilder
.fromConnectionFactory(lettuceConnectionFactory);
@SuppressWarnings("serial")
Set<String> cacheNames = new HashSet<String>() {
{
add("codeNameCache");
}
};
builder.initialCacheNames(cacheNames);
return builder.build();
}
/**
* RedisTemplate配置
*/
@Bean
public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory lettuceConnectionFactory) {
// 设置序列化
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<Object>(
Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
// 配置redisTemplate
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();
redisTemplate.setConnectionFactory(lettuceConnectionFactory);
RedisSerializer<?> stringSerializer = new StringRedisSerializer();
redisTemplate.setKeySerializer(stringSerializer);// key序列化
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);// value序列化
redisTemplate.setHashKeySerializer(stringSerializer);// Hash key序列化
redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);// Hash value序列化
redisTemplate.afterPropertiesSet();
return redisTemplate;
}}
package top.lrshuai.googlecheck.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.ParameterBuilder;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Parameter;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;import java.util.ArrayList;
import java.util.List;@Configuration
@EnableSwagger2
public class SwaggerConfig implements WebMvcConfigurer {
@Bean
public Docket ProductApi() {
//可以添加多个header或参数
ParameterBuilder aParameterBuilder = new ParameterBuilder();
aParameterBuilder.parameterType(“header”) //参数类型支持header, cookie, body, query etc
.name(“token”) //参数名
.defaultValue("") //默认值
.description(“用户token”)
.modelRef(new ModelRef(“string”))//指定参数值的类型
.required(false).build(); //非必需,这里是全局配置,然而在登陆的时候是不用验证的
List aParameters = new ArrayList();
aParameters.add(aParameterBuilder.build());
return new Docket(DocumentationType.SWAGGER_2).apiInfo(productApiInfo())
.groupName(“v1”).select()
.apis(RequestHandlerSelectors.basePackage(“top.lrshuai”))
.build().globalOperationParameters(aParameters);
}private ApiInfo productApiInfo() {
ApiInfo apiInfo = new ApiInfoBuilder()
.title("接口文档")
.version("1.0.0")
.build();
return apiInfo;
}
/**
* 防止@EnableMvc把默认的静态资源路径覆盖了,手动设置的方式
* @param registry
*/
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("swagger-ui.html")
.addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/");
registry.addResourceHandler("/**")
.addResourceLocations("classpath:/static/");
}}
package top.lrshuai.googlecheck.config;import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import top.lrshuai.googlecheck.interceptor.LoginIntercept;@Slf4j
@Component
public class WebMvcConfig implements WebMvcConfigurer {@Autowired
private LoginIntercept loginIntercept;
@Override
public void addInterceptors(InterceptorRegistry registry) {
log.info("添加拦截");
registry.addInterceptor(loginIntercept);
}}
controller
package top.lrshuai.googlecheck.controller;import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import top.lrshuai.googlecheck.annotation.NeedLogin;
import top.lrshuai.googlecheck.base.BaseController;
import top.lrshuai.googlecheck.common.Result;
import top.lrshuai.googlecheck.dto.GoogleDTO;
import top.lrshuai.googlecheck.dto.LoginDTO;
import top.lrshuai.googlecheck.service.UserService;
import top.lrshuai.googlecheck.utils.QRCodeUtil;import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.OutputStream;@Controller
@RequestMapping("/user")
@Api(tags = “用户模块”)
public class UserController extends BaseController {@Autowired
private UserService userService;
@GetMapping("/register")
@ApiOperation("1、注册")
@ResponseBody
public Result register(LoginDTO dto) throws Exception {
return userService.register(dto);
}
@GetMapping("/login")
@ApiOperation("2、登录")
@ResponseBody
public Result login(LoginDTO dto)throws Exception{
return userService.login(dto);
}
@GetMapping("/generateGoogleSecret")
@ResponseBody
@NeedLogin
@ApiOperation("3、生成google密钥")
public Result generateGoogleSecret()throws Exception{
return userService.generateGoogleSecret(this.getUser());
}
/**
* 注意:这个需要地址栏请求,因为返回的是一个流
* 注意:这个需要地址栏请求,因为返回的是一个流
* 注意:这个需要地址栏请求,因为返回的是一个流
* 显示一个二维码图片
* @param secretQrCode generateGoogleSecret接口返回的:secretQrCode
* @param response
* @throws Exception
*/
@GetMapping("/genQrCode")
@ApiOperation("4、生成二维码,这个去地址栏请求,不要用Swagger-ui请求")
public void genQrCode(String secretQrCode, HttpServletResponse response) throws Exception{
response.setContentType("image/png");
OutputStream stream = response.getOutputStream();
QRCodeUtil.encode(secretQrCode,stream);
}
@GetMapping("/bindGoogle")
@ResponseBody
@NeedLogin
@ApiOperation("5、绑定google验证")
public Result bindGoogle(GoogleDTO dto)throws Exception{
return userService.bindGoogle(dto,this.getUser(),this.getRequest());
}
@GetMapping("/googleLogin")
@ResponseBody
@NeedLogin
@ApiOperation("6、google登录")
public Result googleLogin(Long code) throws Exception{
return userService.googleLogin(code,this.getUser(),this.getRequest());
}
@GetMapping("/getData")
@NeedLogin(google = true)
@ApiOperation("7、获取数据")
@ResponseBody
public Result getData()throws Exception{
return userService.getData();
}}
dto
package top.lrshuai.googlecheck.dto;import lombok.Data;
@Data
public class GoogleDTO {
/**
* google密钥
/
private String secret;
/*
* 手机上显示的验证码
*/
private Long code;
}
package top.lrshuai.googlecheck.dto;import lombok.Data;
@Data
public class LoginDTO {
private String username;
private String password;
}entity
package top.lrshuai.googlecheck.entity;import lombok.Data;
/**
• 简单的用户类
•
/
@Data
public class User {
/*
* 用户ID ,唯一标识
/
private String userId;
/*
* 用户名
/
private String username;
/*
* 登录密码
*/
private String password;/**
* google 验证的 密钥
*/
private String googleSecret;}
intercept
package top.lrshuai.googlecheck.interceptor;import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import top.lrshuai.googlecheck.annotation.NeedLogin;
import top.lrshuai.googlecheck.common.ApiResultEnum;
import top.lrshuai.googlecheck.common.CacheEnum;
import top.lrshuai.googlecheck.common.Consts;
import top.lrshuai.googlecheck.common.Result;
import top.lrshuai.googlecheck.entity.User;
import top.lrshuai.googlecheck.utils.Tools;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;/**
• 登录拦截
*/
@Slf4j
@Component
public class LoginIntercept extends HandlerInterceptorAdapter {
@Autowired
private RedisTemplate<String,Object> redisTemplate;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
/**
* isAssignableFrom() 判定此 Class 对象所表示的类或接口与指定的 Class 参数所表示的类或接口是否相同,或是否是其超类或超接口
* isAssignableFrom 与instanceof 区别
* isAssignableFrom()方法是判断是否为某个类的父类
* instanceof 关键字是判断是否某个类的子类
*/
if(handler.getClass().isAssignableFrom(HandlerMethod.class)){
//HandlerMethod 封装方法定义相关的信息,如类,方法,参数等
HandlerMethod handlerMethod = (HandlerMethod) handler;
Method method = handlerMethod.getMethod();
NeedLogin needLogin = getTagAnnotation(method, NeedLogin.class);
if(needLogin != null){
//登录校验
if(needLogin.login() && !isLogin(request,CacheEnum.LOGIN)){
resonseOut(response,Result.error(ApiResultEnum.AUTH_LGOIN_NOT_VALID));
return false;
}//google校验
if(needLogin.google() && !isGoogle(request,CacheEnum.GOOGLE)){
resonseOut(response,Result.error(ApiResultEnum.AUTH_GOOGLE_NOT_FOUND));
return false;
}
}
}
return super.preHandle(request, response, handler);}
/**
• 检查是否 登录或者google验证
• @param request
• @param cacheEnum
• @return
*/
public boolean isGoogle(HttpServletRequest request,CacheEnum cacheEnum){
String tokenKey = Tools.getTokenKey(request,cacheEnum);
String googleStatus = (String) redisTemplate.opsForValue().get(tokenKey);
if(googleStatus != null && googleStatus.equalsIgnoreCase(Consts.SUCCESS)){
return true;
}
return false;
}/**
• 是否登录
• @param request
• @param cacheEnum
• @return
*/
public boolean isLogin(HttpServletRequest request,CacheEnum cacheEnum){
String tokenKey = Tools.getTokenKey(request,cacheEnum);
User user = (User) redisTemplate.opsForValue().get(tokenKey);
if(user == null){
return false;
}
return true;
}/**
• 获取目标注解
• 如果方法上有注解就返回方法上的注解配置,否则类上的
• @param method
• @param annotationClass
• @param
• @return
*/
public A getTagAnnotation(Method method, Class annotationClass) {
// 获取方法中是否包含注解
Annotation methodAnnotate = method.getAnnotation(annotationClass);
//获取 类中是否包含注解,也就是controller 是否有注解
Annotation classAnnotate = method.getDeclaringClass().getAnnotation(annotationClass);
return (A) (methodAnnotate!= null?methodAnnotate:classAnnotate);
}/**
• 回写给客户端
• @param response
• @param result
• @throws IOException
*/
private void resonseOut(HttpServletResponse response, Result result) throws IOException {
response.setCharacterEncoding(“UTF-8”);
response.setContentType(“application/json; charset=utf-8”);
PrintWriter out = null ;
String json = JSONObject.toJSON(result).toString();
out = response.getWriter();
out.append(json);
}
}
filter
package top.lrshuai.fli.filter;import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;/**
*• 使用注解标注过滤器
• @WebFilter将一个实现了javax.servlet.Filter接口的类定义为过滤器
• 属性filterName 声明过滤器的名称,可选
• 属性urlPatterns指定要过滤 的URL模式,这是一个数组参数,可以指定多个。也可使用属性value来声明.(指定要过滤的URL模式是必选属性)
/
@WebFilter(filterName=“myFilter”,urlPatterns={"/"})
public class MyFilter implements Filter{
@Override
public void destroy() {
System.out.println(“myfilter 的 销毁方法”);
}
@Override
public void doFilter(ServletRequest arg0, ServletResponse arg1, FilterChain chain)
throws IOException, ServletException {
System.out.println(“myfilter 的 过滤方法。这里可以执行过滤操作”);
//继续下一个拦截器
chain.doFilter(arg0, arg1);
}
@Override
public void init(FilterConfig arg0) throws ServletException {
System.out.println(“myfilter 的 初始化方法”);
}}
service
package top.lrshuai.googlecheck.service;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import top.lrshuai.encryption.MDUtil;
import top.lrshuai.googlecheck.common.*;
import top.lrshuai.googlecheck.dto.GoogleDTO;
import top.lrshuai.googlecheck.dto.LoginDTO;
import top.lrshuai.googlecheck.entity.User;
import top.lrshuai.googlecheck.utils.GoogleAuthenticator;
import top.lrshuai.googlecheck.utils.Tools;import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;@Service
public class UserService {@Autowired
private RedisTemplate<String,Object> redisTemplate;
/**
* 获取缓存中的数据
* @return
*/
public Result getData(){
Map<String,Object> data = new HashMap<>();
setData(CacheKey.REGISTER_USER_KEY,data);
setData(CacheKey.TOKEN_KEY_LOGIN_KEY,data);
return Result.ok(data);
}
public void setData(String keyword,Map<String,Object> data){
Set<String> keys = redisTemplate.keys(keyword);
Iterator<String> iterator = keys.iterator();
while (iterator.hasNext()){
String key = iterator.next();
data.put(key,redisTemplate.opsForValue().get(key));
}
}
/**
* 注册
* @param dto
* @return
* @throws Exception
*/
public Result register(LoginDTO dto) throws Exception {
User user = new User();
user.setUserId(Tools.getUUID());
user.setUsername(dto.getUsername());
user.setPassword(MDUtil.bcMD5(dto.getPassword()));
addUser(user);
return Result.ok();
}
//获取用户
public User getUser(String username){
User cacheUser = (User) redisTemplate.opsForValue().get(String.format(CacheKey.REGISTER_USER, username));
return cacheUser;
}
//添加注册用户
public void addUser(User user){
if(user == null) throw new ApiException(ApiResultEnum.ERROR_NULL);
User isRepeat = getUser(user.getUsername());
if(isRepeat != null ){
throw new ApiException(ApiResultEnum.USER_IS_EXIST);
}
redisTemplate.opsForValue().set(String.format(CacheKey.REGISTER_USER, user.getUsername()),user,1, TimeUnit.DAYS);
}
//更新token用户
public void updateUser(User user,HttpServletRequest request){
if(user == null) throw new ApiException(ApiResultEnum.ERROR_NULL);
redisTemplate.opsForValue().set(Tools.getTokenKey(request,CacheEnum.LOGIN),user,1, TimeUnit.DAYS);
}
/**
* 登录
* @param dto
* @return
* @throws Exception
*/
public Result login(LoginDTO dto) throws Exception {
User user = getUser(dto.getUsername());
if(user == null){
throw new ApiException(ApiResultEnum.USER_NOT_EXIST);
}
if(!user.getPassword().equals(MDUtil.bcMD5(dto.getPassword()))){
throw new ApiException(ApiResultEnum.USERNAME_OR_PASSWORD_IS_WRONG);
}
//随机生成token
String token = Tools.getUUID();
redisTemplate.opsForValue().set(String.format(CacheKey.TOKEN_KEY_LOGIN,token),user,1,TimeUnit.DAYS);
Map<String,Object> data = new HashMap<>();
data.put(Consts.TOKEN,token);
return Result.ok(data);
}
/**
* 生成Google 密钥
* secret:密钥
* secretQrCode:Google Authenticator 扫描条形码的内容
* @param user
* @return
*/
public Result generateGoogleSecret(User user){
//Google密钥
String randomSecretKey = GoogleAuthenticator.getRandomSecretKey();
String googleAuthenticatorBarCode = GoogleAuthenticator.getGoogleAuthenticatorBarCode(randomSecretKey, user.getUsername(), "https://www.lrshuai.top");
Map<String,Object> data = new HashMap<>();
//Google密钥
data.put("secret",randomSecretKey);
//用户二维码内容
data.put("secretQrCode",googleAuthenticatorBarCode);
return Result.ok(data);
}
/**
* 绑定Google
* @param dto
* @param user
* @return
*/
public Result bindGoogle(GoogleDTO dto, User user, HttpServletRequest request){
if(!StringUtils.isEmpty(user.getGoogleSecret())){
throw new ApiException(ApiResultEnum.GOOGLE_IS_BIND);
}
boolean isTrue = GoogleAuthenticator.check_code(dto.getSecret(), dto.getCode(), System.currentTimeMillis());
if(!isTrue){
throw new ApiException(ApiResultEnum.GOOGLE_CODE_NOT_MATCH);
}
User cacheUser = getUser(user.getUsername());
cacheUser.setGoogleSecret(dto.getSecret());
updateUser(cacheUser,request);
return Result.ok();
}
/**
* Google登录
* @param code
* @param user
* @return
*/
public Result googleLogin(Long code,User user,HttpServletRequest request){
if(StringUtils.isEmpty(user.getGoogleSecret())){
throw new ApiException(ApiResultEnum.GOOGLE_NOT_BIND);
}
boolean isTrue = GoogleAuthenticator.check_code(user.getGoogleSecret(), code, System.currentTimeMillis());
if(!isTrue){
throw new ApiException(ApiResultEnum.GOOGLE_CODE_NOT_MATCH);
}
redisTemplate.opsForValue().set(Tools.getTokenKey(request,CacheEnum.GOOGLE),Consts.SUCCESS,1,TimeUnit.DAYS);
return Result.ok();
}}
uitls
package top.lrshuai.googlecheck.utils;import com.google.zxing.BarcodeFormat;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.WriterException;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import org.apache.commons.codec.binary.Base32;
import org.apache.commons.codec.binary.Hex;import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;public class GoogleAuthenticator {
public static String getRandomSecretKey() {
SecureRandom random = new SecureRandom();
byte[] bytes = new byte[20];
random.nextBytes(bytes);
Base32 base32 = new Base32();
String secretKey = base32.encodeToString(bytes);
// make the secret key more human-readable by lower-casing and
// inserting spaces between each group of 4 characters
return secretKey.toUpperCase(); // .replaceAll("(.{4})(?=.{4})", "$1 ");
}public static String getTOTPCode(String secretKey) {
String normalizedBase32Key = secretKey.replace(" ", "").toUpperCase();
Base32 base32 = new Base32();
byte[] bytes = base32.decode(normalizedBase32Key);
String hexKey = Hex.encodeHexString(bytes);
long time = (System.currentTimeMillis() / 1000) / 30;
String hexTime = Long.toHexString(time);
return TOTP.generateTOTP(hexKey, hexTime, "6");
}
public static String getGoogleAuthenticatorBarCode(String secretKey,
String account, String issuer) {
String normalizedBase32Key = secretKey.replace(" ", "").toUpperCase();
try {
return "otpauth://totp/"
+ URLEncoder.encode(issuer + ":" + account, "UTF-8")
.replace("+", "%20")
+ "?secret="
+ URLEncoder.encode(normalizedBase32Key, "UTF-8").replace(
"+", "%20") + "&issuer="
+ URLEncoder.encode(issuer, "UTF-8").replace("+", "%20");
} catch (UnsupportedEncodingException e) {
throw new IllegalStateException(e);
}
}
public static void createQRCode(String barCodeData, String filePath,
int height, int width) throws WriterException, IOException {
BitMatrix matrix = new MultiFormatWriter().encode(barCodeData,
BarcodeFormat.QR_CODE, width, height);
try (FileOutputStream out = new FileOutputStream(filePath)) {
MatrixToImageWriter.writeToStream(matrix, "png", out);
}
}
static int window_size = 3; // default 3 - max 17 (from google docs)最多可偏移的时间
/**
* set the windows size. This is an integer value representing the number of
* 30 second windows we allow The bigger the window, the more tolerant of
* clock skew we are.
*
* @param s
* window size - must be >=1 and <=17. Other values are ignored
*/
public static void setWindowSize(int s) {
if (s >= 1 && s <= 17)
window_size = s;
}
/**
* Check the code entered by the user to see if it is valid
*
* @param secret
* The users secret.
* @param code
* The code displayed on the users device
* @param timeMsec
* The time in msec (System.currentTimeMillis() for example)
* @return
*/
public static boolean check_code(String secret, long code, long timeMsec) {
Base32 codec = new Base32();
byte[] decodedKey = codec.decode(secret);
// convert unix msec time into a 30 second "window"
// this is per the TOTP spec (see the RFC for details)
long t = (timeMsec / 1000L) / 30L;
// Window is used to check codes generated in the near past.
// You can use this value to tune how far you're willing to go.
for (int i = -window_size; i <= window_size; ++i) {
long hash;
try {
hash = verify_code(decodedKey, t + i);
} catch (Exception e) {
// Yes, this is bad form - but
// the exceptions thrown would be rare and a static
// configuration problem
// e.printStackTrace();
throw new RuntimeException(e.getMessage());
// return false;
}
if (hash == code) {
return true;
}
}
// The validation code is invalid.
return false;
}
private static int verify_code(byte[] key, long t)
throws NoSuchAlgorithmException, InvalidKeyException {
byte[] data = new byte[8];
long value = t;
for (int i = 8; i-- > 0; value >>>= 8) {
data[i] = (byte) value;
}
SecretKeySpec signKey = new SecretKeySpec(key, "HmacSHA1");
Mac mac = Mac.getInstance("HmacSHA1");
mac.init(signKey);
byte[] hash = mac.doFinal(data);
int offset = hash[20 - 1] & 0xF;
// We're using a long because Java hasn't got unsigned int.
long truncatedHash = 0;
for (int i = 0; i < 4; ++i) {
truncatedHash <<= 8;
// We are dealing with signed bytes:
// we just keep the first byte.
truncatedHash |= (hash[offset + i] & 0xFF);
}
truncatedHash &= 0x7FFFFFFF;
truncatedHash %= 1000000;
return (int) truncatedHash;
}}
package top.lrshuai.googlecheck.utils;import java.awt.BasicStroke;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Shape;
import java.awt.geom.RoundRectangle2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.OutputStream;
import java.util.Hashtable;
import java.util.Random;import javax.imageio.ImageIO;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.BinaryBitmap;
import com.google.zxing.DecodeHintType;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatReader;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.Result;
import com.google.zxing.client.j2se.BufferedImageLuminanceSource;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.HybridBinarizer;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;/**
• 二维码工具类
•
*/
public class QRCodeUtil {
private static final String CHARSET = “utf-8”;
private static final String FORMAT = “JPG”;
// 二维码尺寸
private static final int QRCODE_SIZE = 300;
// LOGO宽度
private static final int LOGO_WIDTH = 60;
// LOGO高度
private static final int LOGO_HEIGHT = 60;private static BufferedImage createImage(String content, String logoPath, boolean needCompress) throws Exception {
Hashtable<EncodeHintType, Object> hints = new Hashtable<EncodeHintType, Object>();
hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
hints.put(EncodeHintType.CHARACTER_SET, CHARSET);
hints.put(EncodeHintType.MARGIN, 1);
BitMatrix bitMatrix = new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, QRCODE_SIZE, QRCODE_SIZE,
hints);
int width = bitMatrix.getWidth();
int height = bitMatrix.getHeight();
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
image.setRGB(x, y, bitMatrix.get(x, y) ? 0xFF000000 : 0xFFFFFFFF);
}
}
if (logoPath == null || "".equals(logoPath)) {
return image;
}
// 插入图片
QRCodeUtil.insertImage(image, logoPath, needCompress);
return image;
}
/**
* 插入LOGO
*
* @param source
* 二维码图片
* @param logoPath
* LOGO图片地址
* @param needCompress
* 是否压缩
* @throws Exception
*/
private static void insertImage(BufferedImage source, String logoPath, boolean needCompress) throws Exception {
File file = new File(logoPath);
if (!file.exists()) {
throw new Exception("logo file not found.");
}
Image src = ImageIO.read(new File(logoPath));
int width = src.getWidth(null);
int height = src.getHeight(null);
if (needCompress) { // 压缩LOGO
if (width > LOGO_WIDTH) {
width = LOGO_WIDTH;
}
if (height > LOGO_HEIGHT) {
height = LOGO_HEIGHT;
}
Image image = src.getScaledInstance(width, height, Image.SCALE_SMOOTH);
BufferedImage tag = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics g = tag.getGraphics();
g.drawImage(image, 0, 0, null); // 绘制缩小后的图
g.dispose();
src = image;
}
// 插入LOGO
Graphics2D graph = source.createGraphics();
int x = (QRCODE_SIZE - width) / 2;
int y = (QRCODE_SIZE - height) / 2;
graph.drawImage(src, x, y, width, height, null);
Shape shape = new RoundRectangle2D.Float(x, y, width, width, 6, 6);
graph.setStroke(new BasicStroke(3f));
graph.draw(shape);
graph.dispose();
}
/**
* 生成二维码(内嵌LOGO)
* 二维码文件名随机,文件名可能会有重复
*
* @param content
* 内容
* @param logoPath
* LOGO地址
* @param destPath
* 存放目录
* @param needCompress
* 是否压缩LOGO
* @throws Exception
*/
public static String encode(String content, String logoPath, String destPath, boolean needCompress) throws Exception {
BufferedImage image = QRCodeUtil.createImage(content, logoPath, needCompress);
mkdirs(destPath);
String fileName = new Random().nextInt(99999999) + "." + FORMAT.toLowerCase();
ImageIO.write(image, FORMAT, new File(destPath + "/" + fileName));
return fileName;
}
/**
* 生成二维码(内嵌LOGO)
* 调用者指定二维码文件名
*
* @param content
* 内容
* @param logoPath
* LOGO地址
* @param destPath
* 存放目录
* @param fileName
* 二维码文件名
* @param needCompress
* 是否压缩LOGO
* @throws Exception
*/
public static String encode(String content, String logoPath, String destPath, String fileName, boolean needCompress) throws Exception {
BufferedImage image = QRCodeUtil.createImage(content, logoPath, needCompress);
mkdirs(destPath);
fileName = fileName.substring(0, fileName.indexOf(".")>0?fileName.indexOf("."):fileName.length())
+ "." + FORMAT.toLowerCase();
ImageIO.write(image, FORMAT, new File(destPath + "/" + fileName));
return fileName;
}
/**
* 当文件夹不存在时,mkdirs会自动创建多层目录,区别于mkdir.
* (mkdir如果父目录不存在则会抛出异常)
* @param destPath
* 存放目录
*/
public static void mkdirs(String destPath) {
File file = new File(destPath);
if (!file.exists() && !file.isDirectory()) {
file.mkdirs();
}
}
/**
* 生成二维码(内嵌LOGO)
*
* @param content
* 内容
* @param logoPath
* LOGO地址
* @param destPath
* 存储地址
* @throws Exception
*/
public static String encode(String content, String logoPath, String destPath) throws Exception {
return QRCodeUtil.encode(content, logoPath, destPath, false);
}
/**
* 生成二维码
*
* @param content
* 内容
* @param destPath
* 存储地址
* @param needCompress
* 是否压缩LOGO
* @throws Exception
*/
public static String encode(String content, String destPath, boolean needCompress) throws Exception {
return QRCodeUtil.encode(content, null, destPath, needCompress);
}
/**
* 生成二维码
*
* @param content
* 内容
* @param destPath
* 存储地址
* @throws Exception
*/
public static String encode(String content, String destPath) throws Exception {
return QRCodeUtil.encode(content, null, destPath, false);
}
/**
* 生成二维码(内嵌LOGO)
*
* @param content
* 内容
* @param logoPath
* LOGO地址
* @param output
* 输出流
* @param needCompress
* 是否压缩LOGO
* @throws Exception
*/
public static void encode(String content, String logoPath, OutputStream output, boolean needCompress)
throws Exception {
BufferedImage image = QRCodeUtil.createImage(content, logoPath, needCompress);
ImageIO.write(image, FORMAT, output);
}
/**
* 生成二维码
*
* @param content
* 内容
* @param output
* 输出流
* @throws Exception
*/
public static void encode(String content, OutputStream output) throws Exception {
QRCodeUtil.encode(content, null, output, false);
}
/**
* 解析二维码
*
* @param file
* 二维码图片
* @return
* @throws Exception
*/
public static String decode(File file) throws Exception {
BufferedImage image;
image = ImageIO.read(file);
if (image == null) {
return null;
}
BufferedImageLuminanceSource source = new BufferedImageLuminanceSource(image);
BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
Result result;
Hashtable<DecodeHintType, Object> hints = new Hashtable<DecodeHintType, Object>();
hints.put(DecodeHintType.CHARACTER_SET, CHARSET);
result = new MultiFormatReader().decode(bitmap, hints);
String resultStr = result.getText();
return resultStr;
}
/**
* 解析二维码
*
* @param path
* 二维码图片地址
* @return
* @throws Exception
*/
public static String decode(String path) throws Exception {
return QRCodeUtil.decode(new File(path));
}
public static void main(String[] args) throws Exception {
String text = "http://www.lrshuai.top?kw=哈哈|新年快乐||哈哈";
//不含Logo
//QRCodeUtil.encode(text, null, "e:\\", true);
//含Logo,不指定二维码图片名
//QRCodeUtil.encode(text, "e:\\csdn.jpg", "e:\\", true);
//含Logo,指定二维码图片名
QRCodeUtil.encode(text, "E:\\lrs_img\\16.png", "e:\\", "qrcode", true);
}}
package top.lrshuai.googlecheck.utils;
import org.springframework.util.StringUtils;
import top.lrshuai.googlecheck.common.CacheEnum;
import top.lrshuai.googlecheck.common.CacheKey;
import top.lrshuai.googlecheck.common.Consts;import javax.servlet.http.HttpServletRequest;
import java.util.UUID;/**
• 工具类
•
*/
public class Tools {public static String getUUID(){
return UUID.randomUUID().toString().replaceAll("-","");
}
/**
* 获取 token
* @param request
* @return
*/
public static String getToken(HttpServletRequest request){
String token = request.getHeader(Consts.TOKEN);
if(StringUtils.isEmpty(token)){
token = request.getParameter(Consts.TOKEN);
}
return token;
}
/**
* 获取 tokenKey
* @param request
* @return
*/
public static String getTokenKey(HttpServletRequest request, CacheEnum cacheEnum){
String token = getToken(request);
System.out.println("token="+token);
if(cacheEnum.equals(CacheEnum.GOOGLE)){
token = String.format(CacheKey.TOKEN_KEY_GOOGLE,token);
}else if(cacheEnum.equals(CacheEnum.LOGIN)){
token = String.format(CacheKey.TOKEN_KEY_LOGIN,token);
}
return token;
}}
package top.lrshuai.googlecheck.utils;import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.lang.reflect.UndeclaredThrowableException;
import java.math.BigInteger;
import java.security.GeneralSecurityException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;/**
• This is an example implementation of the OATH TOTP algorithm. Visit
• www.openauthentication.org for more information.
•
• @author Johan Rydell, PortWise, Inc.
*/public class TOTP {
private TOTP() {
}
/**
* This method uses the JCE to provide the crypto algorithm. HMAC computes a
* Hashed Message Authentication Code with the crypto hash algorithm as a
* parameter.
*
* @param crypto
* : the crypto algorithm (HmacSHA1, HmacSHA256, HmacSHA512)
* @param keyBytes
* : the bytes to use for the HMAC key
* @param text
* : the message or text to be authenticated
*/
private static byte[] hmac_sha(String crypto, byte[] keyBytes, byte[] text) {
try {
Mac hmac;
hmac = Mac.getInstance(crypto);
SecretKeySpec macKey = new SecretKeySpec(keyBytes, "RAW");
hmac.init(macKey);
return hmac.doFinal(text);
} catch (GeneralSecurityException gse) {
throw new UndeclaredThrowableException(gse);
}
}
/**
* This method converts a HEX string to Byte[]
*
* @param hex
* : the HEX string
*
* @return: a byte array
*/
private static byte[] hexStr2Bytes(String hex) {
// Adding one byte to get the right conversion
// Values starting with "0" can be converted
byte[] bArray = new BigInteger("10" + hex, 16).toByteArray();
// Copy all the REAL bytes, not the "first"
byte[] ret = new byte[bArray.length - 1];
for (int i = 0; i < ret.length; i++)
ret[i] = bArray[i + 1];
return ret;
}
private static final int[] DIGITS_POWER
// 0 1 2 3 4 5 6 7 8
= { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000 };
/**
* This method generates a TOTP value for the given set of parameters.
*
* @param key
* : the shared secret, HEX encoded
* @param time
* : a value that reflects a time
* @param returnDigits
* : number of digits to return
*
* @return: a numeric String in base 10 that includes
* {@link truncationDigits} digits
*/
public static String generateTOTP(String key, String time,
String returnDigits) {
return generateTOTP(key, time, returnDigits, "HmacSHA1");
}
/**
* This method generates a TOTP value for the given set of parameters.
*
* @param key
* : the shared secret, HEX encoded
* @param time
* : a value that reflects a time
* @param returnDigits
* : number of digits to return
*
* @return: a numeric String in base 10 that includes
* {@link truncationDigits} digits
*/
public static String generateTOTP256(String key, String time,
String returnDigits) {
return generateTOTP(key, time, returnDigits, "HmacSHA256");
}
/**
* This method generates a TOTP value for the given set of parameters.
*
* @param key
* : the shared secret, HEX encoded
* @param time
* : a value that reflects a time
* @param returnDigits
* : number of digits to return
*
* @return: a numeric String in base 10 that includes
* {@link truncationDigits} digits
*/
public static String generateTOTP512(String key, String time,
String returnDigits) {
return generateTOTP(key, time, returnDigits, "HmacSHA512");
}
/**
* This method generates a TOTP value for the given set of parameters.
*
* @param key
* : the shared secret, HEX encoded
* @param time
* : a value that reflects a time
* @param returnDigits
* : number of digits to return
* @param crypto
* : the crypto function to use
*
* @return: a numeric String in base 10 that includes
* {@link truncationDigits} digits
*/
public static String generateTOTP(String key, String time,
String returnDigits, String crypto) {
int codeDigits = Integer.decode(returnDigits).intValue();
String result = null;
// Using the counter
// First 8 bytes are for the movingFactor
// Compliant with base RFC 4226 (HOTP)
while (time.length() < 16)
time = "0" + time;
// Get the HEX in a Byte[]
byte[] msg = hexStr2Bytes(time);
byte[] k = hexStr2Bytes(key);
byte[] hash = hmac_sha(crypto, k, msg);
// put selected bytes into result int
int offset = hash[hash.length - 1] & 0xf;
int binary = ((hash[offset] & 0x7f) << 24)
| ((hash[offset + 1] & 0xff) << 16)
| ((hash[offset + 2] & 0xff) << 8) | (hash[offset + 3] & 0xff);
int otp = binary % DIGITS_POWER[codeDigits];
result = Integer.toString(otp);
while (result.length() < codeDigits) {
result = "0" + result;
}
return result;
}
public static void main(String[] args) {
// Seed for HMAC-SHA1 - 20 bytes
String seed = "3132333435363738393031323334353637383930";
// Seed for HMAC-SHA256 - 32 bytes
String seed32 = "3132333435363738393031323334353637383930"
+ "313233343536373839303132";
// Seed for HMAC-SHA512 - 64 bytes
String seed64 = "3132333435363738393031323334353637383930"
+ "3132333435363738393031323334353637383930"
+ "3132333435363738393031323334353637383930" + "31323334";
long T0 = 0;
long X = 30;
long testTime[] = { 59L, 1111111109L, 1111111111L, 1234567890L,
2000000000L, 20000000000L };
String steps = "0";
DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
df.setTimeZone(TimeZone.getTimeZone("UTC"));
try {
System.out.println("+---------------+-----------------------+"
+ "------------------+--------+--------+");
System.out.println("| Time(sec) | Time (UTC format) "
+ "| Value of T(Hex) | TOTP | Mode |");
System.out.println("+---------------+-----------------------+"
+ "------------------+--------+--------+");
for (int i = 0; i < testTime.length; i++) {
long T = (testTime[i] - T0) / X;
steps = Long.toHexString(T).toUpperCase();
while (steps.length() < 16)
steps = "0" + steps;
String fmtTime = String.format("%1$-11s", testTime[i]);
String utcTime = df.format(new Date(testTime[i] * 1000));
System.out.print("| " + fmtTime + " | " + utcTime + " | "
+ steps + " |");
System.out.println(generateTOTP(seed, steps, "8", "HmacSHA1")
+ "| SHA1 |");
System.out.print("| " + fmtTime + " | " + utcTime + " | "
+ steps + " |");
System.out.println(generateTOTP(seed32, steps, "8",
"HmacSHA256") + "| SHA256 |");
System.out.print("| " + fmtTime + " | " + utcTime + " | "
+ steps + " |");
System.out.println(generateTOTP(seed64, steps, "8",
"HmacSHA512") + "| SHA512 |");
System.out.println("+---------------+-----------------------+"
+ "------------------+--------+--------+");
}
} catch (final Exception e) {
System.out.println("Error : " + e);
}
}}
package top.lrshuai.jwt.util;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
import top.lrshuai.jwt.entity.RSA256Key;import java.security.Key;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.HashMap;
import java.util.Map;/**
• 参考地址:
*/
public class CreateSecrteKey {
public static final String KEY_ALGORITHM = “RSA”;
private static final String PUBLIC_KEY = “RSAPublicKey”;
private static final String PRIVATE_KEY = “RSAPrivateKey”;
private static RSA256Key rsa256Key;
//获得公钥
public static String getPublicKey(Map<String, Object> keyMap) throws Exception {
//获得map中的公钥对象 转为key对象
Key key = (Key) keyMap.get(PUBLIC_KEY);
//byte[] publicKey = key.getEncoded();
//编码返回字符串
return encryptBASE64(key.getEncoded());
}
public static String getPublicKey(RSA256Key rsa256Key) throws Exception {
//获得map中的公钥对象 转为key对象
Key key = rsa256Key.getPublicKey();
//byte[] publicKey = key.getEncoded();
//编码返回字符串
return encryptBASE64(key.getEncoded());
}
//获得私钥
public static String getPrivateKey(Map<String, Object> keyMap) throws Exception {
//获得map中的私钥对象 转为key对象
Key key = (Key) keyMap.get(PRIVATE_KEY);
//byte[] privateKey = key.getEncoded();
//编码返回字符串
return encryptBASE64(key.getEncoded());
}
//获得私钥
public static String getPrivateKey(RSA256Key rsa256Key) throws Exception {
//获得map中的私钥对象 转为key对象
Key key = rsa256Key.getPrivateKey();
//byte[] privateKey = key.getEncoded();
//编码返回字符串
return encryptBASE64(key.getEncoded());
}
//解码返回byte
public static byte[] decryptBASE64(String key) throws Exception {
return (new BASE64Decoder()).decodeBuffer(key);
}
//编码返回字符串
public static String encryptBASE64(byte[] key) throws Exception {
return (new BASE64Encoder()).encodeBuffer(key);
}
//map对象中存放公私钥
public static Map<String, Object> initKey() throws Exception {
// /** RSA算法要求有一个可信任的随机数源 */
//获得对象 KeyPairGenerator 参数 RSA 1024个字节
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
keyPairGen.initialize(1024);
//通过对象 KeyPairGenerator 生成密匙对 KeyPair
KeyPair keyPair = keyPairGen.generateKeyPair();//通过对象 KeyPair 获取RSA公私钥对象RSAPublicKey RSAPrivateKey
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
//公私钥对象存入map中
Map<String, Object> keyMap = new HashMap<String, Object>(2);
keyMap.put(PUBLIC_KEY, publicKey);
keyMap.put(PRIVATE_KEY, privateKey);
return keyMap;}
/**
• 获取公私钥
• @return
• @throws Exception
*/
public static synchronized RSA256Key getRSA256Key() throws Exception {
if(rsa256Key == null){
synchronized (RSA256Key.class){
if(rsa256Key == null) {
rsa256Key = new RSA256Key();
Map<String, Object> map = initKey();
rsa256Key.setPrivateKey((RSAPrivateKey) map.get(CreateSecrteKey.PRIVATE_KEY));
rsa256Key.setPublicKey((RSAPublicKey) map.get(CreateSecrteKey.PUBLIC_KEY));
}
}
}
return rsa256Key;
}public static void main(String[] args) {
Map<String, Object> keyMap;
try {
keyMap = initKey();
String publicKey = getPublicKey(keyMap);
System.out.println(“公钥:\n”+publicKey);
String privateKey = getPrivateKey(keyMap);
System.out.println(“私钥:\n”+privateKey);
} catch (Exception e) {
e.printStackTrace();
}
}
}
package top.lrshuai.jwt.util;import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;public class DateUtils {
private final static SimpleDateFormat sdfYear = new SimpleDateFormat(“yyyy”);
private final static SimpleDateFormat sdfDay = new SimpleDateFormat(
“yyyy-MM-dd”);
private final static SimpleDateFormat sdfDays = new SimpleDateFormat(
“yyyyMMdd”);
private final static SimpleDateFormat sdfTime = new SimpleDateFormat(
“yyyy-MM-dd HH:mm:ss”);
/**
* 获取当天的开始时间
* @return
/
public static Date getDayBegin() {
Calendar cal = new GregorianCalendar();
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
return cal.getTime();
}
/*
* 获取当天的结束时间
* @return
/
public static Date getDayEnd() {
Calendar cal = new GregorianCalendar();
cal.set(Calendar.HOUR_OF_DAY, 23);
cal.set(Calendar.MINUTE, 59);
cal.set(Calendar.SECOND, 59);
return cal.getTime();
}
/*
* 获取昨天的开始时间
* @return
/
public static Date getBeginDayOfYesterday() {
Calendar cal = new GregorianCalendar();
cal.setTime(getDayBegin());
cal.add(Calendar.DAY_OF_MONTH, -1);
return cal.getTime();
}
/*
* 获取昨天的结束时间
* @return
*/
public static Date getEndDayOfYesterDay() {
Calendar cal = new GregorianCalendar();
cal.setTime(getDayEnd());
cal.add(Calendar.DAY_OF_MONTH, -1);
return cal.getTime();
}/**
* 获取明天的开始时间
* @return
*/
public static Date getBeginDayOfTomorrow() {
Calendar cal = new GregorianCalendar();
cal.setTime(getDayBegin());
cal.add(Calendar.DAY_OF_MONTH, 1);
return cal.getTime();
}
/**
* 获取明天的结束时间
* @return
*/
public static Date getEndDayOfTomorrow() {
Calendar cal = new GregorianCalendar();
cal.setTime(getDayEnd());
cal.add(Calendar.DAY_OF_MONTH, 1);
return cal.getTime();
}
/**
* 获取本周的开始时间
* @return
*/
public static Date getBeginDayOfWeek() {
Date date = new Date();
Calendar cal = Calendar.getInstance();
cal.setTime(date);
int dayofweek = cal.get(Calendar.DAY_OF_WEEK);
if (dayofweek == 1) {
dayofweek += 7;
}
cal.add(Calendar.DATE, 2 - dayofweek);
return getDayStartTime(cal.getTime());
}
/**
* 获取本周的结束时间
* @return
*/
public static Date getEndDayOfWeek() {
Calendar cal = Calendar.getInstance();
cal.setTime(getBeginDayOfWeek());
cal.add(Calendar.DAY_OF_WEEK, 6);
Date weekEndSta = cal.getTime();
return getDayEndTime(weekEndSta);
}
/**
* 获取本月的开始时间
* @return
*/
public static Date getBeginDayOfMonth() {
Calendar calendar = Calendar.getInstance();
calendar.set(getNowYear(), getNowMonth() - 1, 1);
return getDayStartTime(calendar.getTime());
}
/**
* 获取本月的结束时间
* @return
*/
public static Date getEndDayOfMonth() {
Calendar calendar = Calendar.getInstance();
calendar.set(getNowYear(), getNowMonth() - 1, 1);
int day = calendar.getActualMaximum(5);
calendar.set(getNowYear(), getNowMonth() - 1, day);
return getDayEndTime(calendar.getTime());
}
/**
* 获取今年是哪一年
* @return
*/
public static Integer getNowYear() {
Date date = new Date();
GregorianCalendar gc = (GregorianCalendar) Calendar.getInstance();
gc.setTime(date);
return Integer.valueOf(gc.get(1));
}
/**
* 获取本月是哪一月
* @return
*/
public static int getNowMonth() {
Date date = new Date();
GregorianCalendar gc = (GregorianCalendar) Calendar.getInstance();
gc.setTime(date);
return gc.get(2) + 1;
}
/**
* 获取某个日期的开始时间
* @param d
* @return
*/
public static Timestamp getDayStartTime(Date d) {
Calendar calendar = Calendar.getInstance();
if (null != d)
calendar.setTime(d);
calendar.set(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH), calendar.get(Calendar.DAY_OF_MONTH), 0,
0, 0);
calendar.set(Calendar.MILLISECOND, 0);
return new Timestamp(calendar.getTimeInMillis());
}
/**
* 获取某个日期的结束时间
* @param d
* @return
*/
public static Timestamp getDayEndTime(Date d) {
Calendar calendar = Calendar.getInstance();
if (null != d)
calendar.setTime(d);
calendar.set(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH), calendar.get(Calendar.DAY_OF_MONTH), 23,
59, 59);
calendar.set(Calendar.MILLISECOND, 999);
return new Timestamp(calendar.getTimeInMillis());
}
/**
* 日期时间偏移
* offset = 1,date=2018-11-02 16:47:00
* 结果:2018-11-03 16:47:00
* @param date
* @param offset
* @return
*/
public static Date offset(Date date, int offset) {
Calendar cal = Calendar.getInstance();
cal.setTime(date);
cal.add(Calendar.DAY_OF_MONTH, offset);
return cal.getTime();
}
// 日期偏移
public static Date offset(Date date, int offset,int calendar) {
Calendar cal = Calendar.getInstance();
cal.setTime(date);
cal.add(calendar, offset);
return cal.getTime();
}}
启动类
package top.lrshuai.googlecheck;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
public class SpringBootGoogleCheckApplication {public static void main(String[] args) {
SpringApplication.run(SpringBootGoogleCheckApplication.class, args);
}}
spring boot 项目嵌入积木报表
转载文章标签 过滤器 jedis interceptor intellij idea spring 文章分类 spring boot 后端开发
-
RuoYi整合积木报表
RuoYi整合积木报表
xml spring .net -
spring boot集成积木报表 缓存配置 spring boot 缓存框架
概述上一节我们讲了SpringBoot整合Redis缓存,这节我们来讲Ehcache。EhCache 是一个纯Java的轻量级进程内缓存框架,具有快速、简单等特点,是Hibernate中默认的缓存提供方。相对于Redis这类可分布式的缓存中间件,Ehcache是属于进程内缓存,和Guava Cache、Caffeine等缓存框架一样都属于堆内存缓存,适合单点使用,不太适合分布式场景。EhCache
ehcache springboot ehcache使用 java cache缓存 jeesit框架如何使用 redis清理缓存 -
Spring Boot集成积木报表 springboot集成lombok
1.相关介绍Lombok是一个通过注解以达到减少代码的Java库,如通过注解的方式减少get,set方法,构造方法等, 同时可以自动化日志变量。2.安装步骤1.添加依赖在pom.xml中添加lombok的依赖<!--lombok依赖--> <dependency> <groupId>org.projectlombok<
Spring Boot集成积木报表 spring boot intellij-idea java User