记录SpringBoot项目下token的使用
项目代码:码云链接
项目技术栈
后端
* SpringBoot + MyBatis
前端
* MiniUi + jQuery
数据库
* MySql
项目集成
一、登录成功后生成token
Tip:此处的token只是用于练习用,暂未考虑安全性
@ApiOperation("用户登录时自动添加登录记录")
@RequestMapping(value = "/login", method = RequestMethod.POST)
public Object login(String userJSON){
String json = userJSON.replace("[","").replace("]","");
User user = (User) JSONArray.parseObject(json, User.class);
String uId = user.getUId();
String detailId = loginDetailService.selectNewDetailId();
LoginDetail detail = new LoginDetail(detailId, uId, new Date());
User loginUser = loginDetailService.insertLoginDetail(detail,user);
if(loginUser != null){
String token = createUserToken(user.getUId(), detailId);
return detailId.concat("-").concat(String.valueOf(loginUser.getUserLevel())).concat("-").concat(token);
}else{
return "fail";
}
}
private String createUserToken(String uId, String detailId) {
UserToken userToken = new UserToken();
String tokenValue = uId.concat("xzk").concat(detailId);
userToken.setTokenKey(uId);
userToken.setTokenValue(tokenValue);
userToken.setCreateTime(new Date());
Date expireTime = new Date();
expireTime.setTime(expireTime.getTime() + 7 * 24 * 60 * 60 * 1000);
userToken.setExpireTime(expireTime);
UserToken token = userTokenService.selectUserTokenByKey(uId);
if(token == null) {
userTokenService.insertUserToken(userToken);
}
else{
userToken.setTokenValue(token.getTokenValue());
userTokenService.updateUserToken(userToken);
}
return userToken.getTokenValue();
}
二、前端保存token
三、token传参
$.ajax({
url: '/user/operationByUser',
type: 'post',
headers : {
"token" : getToken(loginUid)
},
data: { userJSON: json, uId: loginUid, detailId: detailId },
cache: false,
success: function (text) {
mini.alert(text,"提示",function(){
if(text.indexOf('成功') >= 0) {
delCookie();
window.open('/', '_top');
}
});
},
error: function (jqXHR, textStatus, errorThrown) {
reLogin();
mini.alert(jqXHR.responseText,"提示",function(){
CloseWindow();
});
}
});
四、后端Filter
1、公共Filter,用于过滤非列表接口请求
package com.productmanage.filter;
import com.productmanage.bean.User;
import com.productmanage.bean.UserToken;
import com.productmanage.service.UserService;
import com.productmanage.service.UserTokenService;
import com.productmanage.util.PropertiesUtil;
import com.productmanage.util.StringUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.annotation.PostConstruct;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Date;
/**
* @Description
* @Author xzkui
* @Date 2021/8/24 10:12
**/
// 过滤器注解,名称以及需要拦截的地址
@WebFilter(filterName = "publicFilter", urlPatterns = {"/user/*", "/turnOver/*", "/product/*", "/notification/*", "/logindetail/*", "/depotitem/*"})
// 过滤器排序,值越小越靠前
@Order(1)
public class PublicFilter extends OncePerRequestFilter {
@Autowired
private UserService userService;
@Autowired
private UserTokenService userTokenService;
private static PublicFilter publicFilter;
// 在非controller层引用servic对象时的解决方案
@PostConstruct
public void init(){
publicFilter = this;
publicFilter.userTokenService = this.userTokenService;
publicFilter.userService = this.userService;
}
// 获取免过滤接口地址
private static String avoidFilterUrl;
static {
try {
avoidFilterUrl = PropertiesUtil.getPropertiesValue("config/project.properties", "avoid-filter-url");
}
catch (IOException e) {
e.printStackTrace();
}
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
// 获取当前请求地址
String requestUrl = request.getRequestURI();
// 使用cookie存放用户信息,每个用户对应一个token
Cookie[] cookies = request.getCookies();
if(cookies == null || requestUrl.indexOf("ByPage") >= 0 || avoidFilterUrl.indexOf(requestUrl) >= 0){
filterChain.doFilter(request, response);
return ;
}
for(Cookie cookie : cookies){
String cookieName = cookie.getName();
User user = userService.selectUserById(cookieName);
if(user != null){
String requestToken = request.getHeader("token");
UserToken userToken = userTokenService.selectUserTokenByKey(user.getUId());
if(StringUtil.isNotBlank(requestToken) && userToken != null) {
if (requestToken.equals(userToken.getTokenValue())) {
if (userToken.getExpireTime() != null && userToken.getExpireTime().after(new Date())) {
filterChain.doFilter(request, response);
}
// token 过期
else {
response.setStatus(401);
response.setCharacterEncoding("UTF-8");
response.getWriter().write("token is expired,please login again");
}
}
// token 错误
else {
response.setStatus(401);
response.setCharacterEncoding("UTF-8");
response.getWriter().write("wrong token!");
}
}
else{
response.setStatus(401);
response.setCharacterEncoding("UTF-8");
response.getWriter().write("token is not exists!");
}
}
}
}
}
2、获取列表数据接口Filter
前端使用的是MiniUi,暂时未找到datagrid获取列表数据时将token添加到headers中,所以将列表数据的请求单独新建一个Filter类
package com.productmanage.filter;
import com.productmanage.bean.User;
import com.productmanage.bean.UserToken;
import com.productmanage.service.UserService;
import com.productmanage.service.UserTokenService;
import com.productmanage.util.StringUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.annotation.PostConstruct;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Date;
/**
* @Description
* @Author xzkui
* @Date 2021/8/24 12:20
**/
@WebFilter(filterName = "commonListFilter", urlPatterns = "/commonListController/*")
@Order(2)
public class CommonListFilter extends OncePerRequestFilter {
@Autowired
private UserService userService;
@Autowired
private UserTokenService userTokenService;
private static CommonListFilter commonListFilter;
@PostConstruct
public void init(){
commonListFilter = this;
commonListFilter.userTokenService = this.userTokenService;
commonListFilter.userService = this.userService;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String token = request.getHeader("token");
if(StringUtil.isNotBlank(token)){
UserToken userToken = userTokenService.selectUserTokenByToken(token);
if(userToken != null && userToken.getExpireTime().after(new Date())){
String uId = userToken.getTokenKey();
User user = userService.selectUserById(uId);
if(user != null){
filterChain.doFilter(request, response);
}
// token对应用户不存在
else{
response.setStatus(401);
response.setCharacterEncoding("UTF-8");
response.getWriter().write("wrong token!");
}
}
// token不存在
else{
response.setStatus(401);
response.setCharacterEncoding("UTF-8");
response.getWriter().write("token is not exists!");
}
}
// token为空
else{
response.setStatus(401);
response.setCharacterEncoding("UTF-8");
response.getWriter().write("token is not exists!");
}
}
}
3、因miniui接口请求机制,部分接口配置为免过滤
- 新建一个配置文件:resource/config/project.properties
- 新建一个工具类PropertiesUtil.java,用于读取配置文件
package com.productmanage.util;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
/**
* @Description
* @Author xzkui
* @Date 2021/8/19 14:20
**/
public class PropertiesUtil {
public static String getPropertiesValue(String file, String key) throws IOException {
String rtnVal = "";
Properties properties = new Properties();
InputStream inputStream = null;
try{
inputStream = PropertiesUtil.class.getClassLoader().getResourceAsStream(file);
properties.load(inputStream);
rtnVal = properties.getProperty(key);
}
catch (Exception e){
e.printStackTrace();
}
finally {
if(inputStream != null){
inputStream.close();
}
}
return rtnVal;
}
}
五、Application添加注解@ServletComponentScan
package com.productmanage;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.cache.annotation.EnableCaching;
@MapperScan("com.productmanage.mapper")
@SpringBootApplication
@ServletComponentScan(basePackages = "com.productmanage.filter")
@EnableCaching
public class ProductmanagementApplication {
public static void main(String[] args) {
SpringApplication.run(ProductmanagementApplication.class, args);
}
}