对于很多系统来说,登陆权限控制是每个系统都具有的,不过实现的方案也多种多样。
下面利用简单的demo来实现使用 Token认证来控制系统的权限访问。
package com.csq.study.springcloud.token.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
*
* @Description: TODO
* @ClassName: AuthToken
* @author chengshengqing 2019年7月2日
* @see TODO
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface AuthToken {
}
package com.csq.study.springcloud.token.configuration;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import com.csq.study.springcloud.token.interceptor.TokemInterceptor;
/**
*
* @Description: TODO
* @ClassName: WebAppConfiguration
* @author chengshengqing 2019年7月2日
* @see TODO
*/
@Configuration
public class WebAppConfiguration implements WebMvcConfigurer {
@Bean
public HandlerInterceptor getUserInterceptor(){
return new TokemInterceptor();
}
//这里与原文不同,在TokemInterceptor拦截器中注入service无效,此法可解决该问题
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(getUserInterceptor()).addPathPatterns("/**");
}
}
package com.csq.study.springcloud.token.controller;
import com.alibaba.fastjson.JSONObject;
import com.csq.study.springcloud.token.UserDao.UserDao;
import com.csq.study.springcloud.token.annotation.AuthToken;
import com.csq.study.springcloud.token.entity.ResponseTemplate;
import com.csq.study.springcloud.token.entity.User;
import com.csq.study.springcloud.token.util.CommonUtil;
import com.csq.study.springcloud.token.util.Md5TokenGenerator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import redis.clients.jedis.Jedis;
/**
*
* @Description: TODO
* @ClassName: welcome
* @author chengshengqing 2019年7月2日
* @see TODO
*/
@RestController
public class TokenController {
Logger logger = LoggerFactory.getLogger(TokenController.class);
@Autowired
private Md5TokenGenerator tokenGenerator;
@Autowired
private UserDao userDao;
@GetMapping("/welcome")
public String Welcome(){
return "welcome TokenController ";
}
@RequestMapping(value = "/login", method = RequestMethod.GET)
public ResponseTemplate Login(String username, String password) {
logger.info("用户名username为:"+username+ "密码password为:"+password);
User user = userDao.getUser(username,password);
logger.info("从数据库查出来的用户user为:"+user);
JSONObject obj = new JSONObject();
if (user != null) {
String token = SetRedisData(username, password);
obj.put("status", "用户登录成功");
obj.put("token", token);
} else {
obj.put("status", "用户登录失败");
}
return ResponseTemplate.builder().code(200).message("用户登录成功").data(obj).build();
}
@RequestMapping(value = "test", method = RequestMethod.GET)
@AuthToken
public ResponseTemplate test() {
logger.info("**************测试start**************");
return ResponseTemplate.builder().code(200).message("测试成功").data("测试数据").build();
}
/**
* 在redis中进行数据的绑定
* @Title: SetRedisData
* @Description: TODO
* @param username
* @param password
* @return
* @author chengshengqing 2019年7月2日
*/
private String SetRedisData(String username, String password) {
//此处主要设置你的redis的ip和端口号,我的redis是在本地
Jedis jedis = new Jedis("127.0.0.1", 6379);
String token = tokenGenerator.generate(username, password);
jedis.set(username, token);
//设置key过期时间,到期会自动删除
jedis.expire(username, CommonUtil.TOKEN_EXPIRE_TIME);
//将token和username以键值对的形式存入到redis中进行双向绑定
jedis.set(token, username);
jedis.expire(token, CommonUtil.TOKEN_EXPIRE_TIME);
Long currentTime = System.currentTimeMillis();
jedis.set(token + username, currentTime.toString());
//用完关闭
jedis.close();
return token;
}
}
package com.csq.study.springcloud.token.interceptor;
import com.alibaba.fastjson.JSONObject;
import com.csq.study.springcloud.token.annotation.AuthToken;
import com.csq.study.springcloud.token.util.CommonUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import redis.clients.jedis.Jedis;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.lang.reflect.Method;
/**
*
* @Description: TODO
* @ClassName: AuthorizationInterceptor
* @author chengshengqing 2019年7月2日
* @see TODO
*/
public class TokemInterceptor implements HandlerInterceptor {
Logger log = LoggerFactory.getLogger(TokemInterceptor.class);
//存放鉴权信息的Header名称,默认是Authorization
private String Authorization = "Authorization";
//鉴权失败后返回的HTTP错误码,默认为401
private int unauthorizedErrorCode = HttpServletResponse.SC_UNAUTHORIZED;
/**
* 存放用户名称和对应的key
*/
public static final String USER_KEY = "USER_KEY";
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (!(handler instanceof HandlerMethod)) {
return true;
}
HandlerMethod handlerMethod = (HandlerMethod) handler;
Method method = handlerMethod.getMethod();
//验证token
if (method.getAnnotation(AuthToken.class) != null || handlerMethod.getBeanType().getAnnotation(AuthToken.class) != null) {
String token = request.getParameter(Authorization);
log.info("获取到的token为: {} ", token);
//此处主要设置你的redis的ip和端口号,我的redis是在本地
Jedis jedis = new Jedis("127.0.0.1", 6379);
String username = null;
if (token != null && token.length() != 0) {
//从redis中根据键token来获取绑定的username
username = jedis.get(token);
log.info("从redis中获取的用户名称为: {}", username);
}
//判断username不为空的时候
if (username != null && !username.trim().equals("")) {
String startBirthTime = jedis.get(token + username);
log.info("生成token的时间为: {}", startBirthTime);
Long time = System.currentTimeMillis() - Long.valueOf(startBirthTime);
log.info("token存在时间为 : {} ms", time);
//重新设置Redis中的token过期时间
if (time > CommonUtil.TOKEN_RESET_TIME) {
jedis.expire(username, CommonUtil.TOKEN_EXPIRE_TIME);
jedis.expire(token, CommonUtil.TOKEN_EXPIRE_TIME);
log.info("重置成功!");
Long newBirthTime = System.currentTimeMillis();
jedis.set(token + username, newBirthTime.toString());
}
//关闭资源
jedis.close();
request.setAttribute(USER_KEY, username);
return true;
} else {
JSONObject jsonObject = new JSONObject();
PrintWriter out = null;
try {
response.setStatus(unauthorizedErrorCode);
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
jsonObject.put("code", response.getStatus());
//鉴权失败后返回的错误信息,默认为401 unauthorized
jsonObject.put("message", HttpStatus.UNAUTHORIZED);
out = response.getWriter();
out.println(jsonObject);
return false;
} catch (Exception e) {
e.printStackTrace();
} finally {
if (null != out) {
out.flush();
out.close();
}
}
}
}
request.setAttribute(USER_KEY, null);
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
package com.csq.study.springcloud.token.util;
public final class CommonUtil {
/**
* redis存储token设置的过期时间,10分钟
*/
public static final Integer TOKEN_EXPIRE_TIME = 60 * 10;
/**
* 设置可以重置token过期时间的时间界限
*/
public static final Integer TOKEN_RESET_TIME = 1000 * 100;
}
package com.csq.study.springcloud.token.util;
import org.springframework.stereotype.Component;
import org.springframework.util.DigestUtils;
@Component
public class Md5TokenGenerator implements TokenGenerator {
@Override
public String generate(String... strings) {
long timestamp = System.currentTimeMillis();
String tokenMeta = "";
for (String s : strings) {
tokenMeta = tokenMeta + s;
}
tokenMeta = tokenMeta + timestamp;
String token = DigestUtils.md5DigestAsHex(tokenMeta.getBytes());
return token;
}
}
package com.csq.study.springcloud.token.util;
import org.springframework.stereotype.Component;
@Component
public interface TokenGenerator {
public String generate(String[] strings);
}
小贴士:整合到自己项目中时,该配置可以省略,否则会导致项目发布构建时报错,报错截图如下:
原因是原来的项目已经用@SpringBootApplication配置过了,从而导致上述报错。
package com.csq.study.springcloud.token;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class TokenApplication {
public static void main(String[] args) {
SpringApplication.run(TokenApplication.class, args);
}
}
启动项目,然后启动redis
再访问:http://localhost:8080/login?username=csq&password=123456
看到如下截图:
然后 我们访问:http://localhost:8080/test?Authorization=43ebef36c1d83f54d2606f10197dcef2
然后我们在稍微改一下那个token:访问: http://localhost:8080/test?Authorization=43ebef36c1d83f54d2606f10197dcef4
看截图:
然后我们去看下控制台输出的截图:
以上说明 :我们的token实现权限控制小demo搭建成功了。
此demo容易出错的地方:
1.redis错误,我们在本地安装的redis一般默认是没有密码的。所以在配置文件中不需要配置密码
2.redis需要在访问项目之前启动
3.在eclipse中lombok的jar包问题有的会导致@Builder注解不能用,可以试试这个方法(请点击查看),就可以解决这个问题
————————————————
版权声明:本文为CSDN博主「努力前行的小coder」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/FindHuni/article/details/94451136