核心是利用com.google.common.cache.LoadingCache 实现定时map,把token作为key,登录名作为value,存入定时map。
controller:
package com.zjzy.controller;
import com.zjzy.po.postgres.Account;
import com.zjzy.response.BaseResponse;
import com.zjzy.service.AccountService;
import com.zjzy.service.TimeTaskService;
import com.zjzy.vo.AccountVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/account")
@Api(tags = "账号操作")
public class AccountApi {
@Autowired
AccountService accountService;
@PostMapping("/register")
@ApiOperation("注册账号")
BaseResponse register(@RequestBody AccountVO accountVO){
accountService.register(accountVO.getName(),accountVO.getPassword());
return new BaseResponse(200,"注册成功!");
}
@PostMapping("/login")
@ApiOperation("登录,返回token")
BaseResponse<String> login(@RequestBody AccountVO accountVO){
String token = accountService.login(accountVO.getName(), accountVO.getPassword());
return new BaseResponse(200,"登录成功!",token);
}
}
service:
package com.zjzy.service;
import com.google.common.cache.LoadingCache;
import com.zjzy.po.postgres.Account;
import com.zjzy.repository.postgres.AccountRepository;
import com.zjzy.utils.GenerateUuidNumberUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;
import java.util.UUID;
@Service
public class AccountService {
@Autowired
AccountRepository accountRepository;
@Autowired
HttpServletResponse response;
@Autowired
LoadingCache loadingCache;
/**
* 注册
* @param name
* @param password
*/
@Transactional//默认捕获RuntimeException
public void register(String name,String password){
Account account=accountRepository.findByUsername(name);
if(account!=null){
throw new RuntimeException("用户名已存在!");
}
account=new Account();
Date cur=new Date();
account.setCreateTime(cur);
account.setDeleted(0);
account.setUpdateTime(cur);
account.setUsername(name);
account.setPassword(DigestUtils.md5DigestAsHex(password.getBytes()));
accountRepository.save(account);
}
/**
*
* @param name
* @param password
* @return 返回token
*/
@Transactional//默认捕获RuntimeException
public String login(String name,String password){
String psw = DigestUtils.md5DigestAsHex(password.getBytes());
Account account=accountRepository.findByUsernameAndPassword(name,psw);
if(account==null){
throw new RuntimeException("账号或密码错误!");
}
String token=UUID.randomUUID().toString();
loadingCache.put(token,name);
return token;
// response.setHeader("Authorization",token);
}
}
配置文件:
package com.zjzy.config;
import com.google.common.cache.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.concurrent.TimeUnit;
@Configuration
public class LoginConfig {
public static String NO_KEY="不存在的key";
//最大用户数
@Value("${login.usersize:100}")
private Integer usersize;
//过期时间(分)
@Value("${login.expire:100}")
private Integer expire;
//定时map
@Bean
LoadingCache createLoadingCache(){
LoadingCache<String, String> cache = CacheBuilder.newBuilder()
.maximumSize(usersize)
.expireAfterAccess(expire, TimeUnit.MINUTES)
.removalListener(new RemovalListener<String, String>() {
@Override
public void onRemoval(RemovalNotification<String, String> removalNotification) {
System.out.println("过时删除的钩子触发了... key ===> " + removalNotification.getKey());
}
}).recordStats().build(new CacheLoader<String, String>() {
// 处理缓存键不存在缓存值时的处理逻辑
@Override
public String load(String key) throws Exception {
return NO_KEY;
}
});
return cache;
}
}
package com.zjzy.config;
import com.zjzy.interceptor.AccountInteceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {//WebMvcConfigurationSuppor自动配置失效
@Autowired
private AccountInteceptor accountInteceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
//注意排除swagger-ui
registry.addInterceptor(accountInteceptor)
.addPathPatterns(
"/**"
)
.excludePathPatterns(
"/**/login/**",
"/**/register/**",
"/**/*.html",
"/**/*.js",
"/**/*.css",
"/**/*.jpg",
"/**/*.mp3",
"/swagger-resources/**",
"/webjars/**",
"/v2/**",
"/swagger-ui.html/**"
);
}
//注意排除swagger-ui
@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/");
}
}
拦截器:
package com.zjzy.interceptor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.cache.LoadingCache;
import com.zjzy.config.LoginConfig;
import com.zjzy.response.BaseResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
@Component
public class AccountInteceptor implements HandlerInterceptor {
@Autowired
LoadingCache<String,String> loadingCache;
@Autowired
HttpServletResponse response;
@Autowired
HttpServletRequest request;
private ObjectMapper objectMapper=new ObjectMapper();
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String authorization = request.getHeader("Authorization");
if(authorization==null|| StringUtils.isEmpty(authorization)||loadingCache.get(authorization)==LoginConfig.NO_KEY){
returnJson(objectMapper.writeValueAsString(new BaseResponse(400,"token错误或已过期")));
return false;
}
String userName=loadingCache.get(authorization);
//用户信息存入Request
request.setAttribute("userName",userName);
return true;
}
private void returnJson(String json){
PrintWriter writer = null;
response.setCharacterEncoding("UTF-8");
response.setContentType("text/json; charset=utf-8");
try {
writer = response.getWriter();
writer.print(json);
} catch (IOException e) {
System.out.println("回写错误!");
} finally {
if (writer != null) {
writer.close();
}
}
}
}
swagger-ui(加消息头):
package com.zjzy.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.ParameterBuilder;
import springfox.documentation.builders.PathSelectors;
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;
//@Profile("dev")
@Configuration
@EnableSwagger2
public class SwaggerConfig {
@Bean
public Docket createRestApi() {
ParameterBuilder ticketPar = new ParameterBuilder();
List<Parameter> pars = new ArrayList<Parameter>();
ticketPar.name("Authorization").description("登录Authorization")
.modelRef(new ModelRef("string")).parameterType("header")
.required(false).build(); //header中的ticket参数非必填,传空也可以
pars.add(ticketPar.build()); //根据每个方法名也知道当前方法在设置什么参数
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo()).select()
//扫描指定包中的swagger注解
.apis(RequestHandlerSelectors.basePackage("com.zjzy.controller"))
//扫描所有有注解的api,用这种方式更灵活
// .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
.paths(PathSelectors.any())
.build().globalOperationParameters(pars);
//************把消息头添加
// .globalOperationParameters(pars);
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("德清气象二期-气象预警系统接口文档")
.description("德清气象二期-气象预警系统接口文档")
.version("2.0.1")
.build();
}
}