不使用shrio安全框架,直接使用token+redis进行鉴权
个人商城的项目,考虑只需要做用户认证,就没用引入shrio等框架。直接往redis存token,然后进行校验,以此进行登录认证
实体类TokenModel:
@Data
public class TokenModel {
private String userId;
private String token;
public TokenModel(String userId,String token){
this.userId = userId;
this.token = token;
}
}
创建注解,在controller层中使用
@autorization 是否需要登录
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Autorization {
}
注解实现:
@Component
public class AuthorizationInterceptor extends HandlerInterceptorAdapter {
@Autowired
private TokenManager manager;
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
//如果不是映射到方法直接通过
if (!(handler instanceof HandlerMethod)) {
response.sendError(401,"请登录");
return true;
}
HandlerMethod handlerMethod = (HandlerMethod) handler;
Method method = handlerMethod.getMethod();
//从header中得到token
String authorization = request.getHeader(Constants.AUTHORIZATION);
//验证token
TokenModel model = manager.getToken(authorization);
if (manager.checkToken(model)) {
//如果token验证成功,将token对应的用户id存在request中,便于之后注入
request.setAttribute(Constants.CURRENT_USER_ID, model.getUserId());
return true;
}
//如果验证token失败,并且方法注明了Authorization,返回401错误
if (method.getAnnotation(Autorization.class) != null) {
response.sendError(401,"请登录");
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
return false;
}
return true;
}
}
@Currentuser 校验用户
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Autorization {
}
注解实现:
@Component
public class CurrentUserMethodArgumentResolver implements HandlerMethodArgumentResolver {
@Autowired
private UserServer userServer;
@Override
public boolean supportsParameter(MethodParameter parameter) {
//如果参数类型是User并且有CurrentUser注解则支持
if (parameter.getParameterType().isAssignableFrom(User.class) &&
parameter.hasParameterAnnotation(CurrentUser.class)) {
return true;
}
return false;
}
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
//取出鉴权时存入的登录用户Id
String currentUserId = (String) webRequest.getAttribute(Constants.CURRENT_USER_ID, RequestAttributes.SCOPE_REQUEST);
if (currentUserId != null) {
//从数据库中查询并返回
return userServer.getUserById(currentUserId);
}
throw new MissingServletRequestPartException(Constants.CURRENT_USER_ID);
}
}
service层,创建token,校验token
@Component
public class TokenManagerImpl implements TokenManager {
@Resource
private RedisTemplate<String, String> redis;
public void setRedis(RedisTemplate redis) {
this.redis = redis;
//泛型设置成Long后必须更改对应的序列化方案
redis.setKeySerializer(new JdkSerializationRedisSerializer());
}
public TokenModel createToken(String userId) {
//使用uuid作为源token
String token = UUID.randomUUID().toString().replace("-", "");
TokenModel model = new TokenModel(userId, token);
//存储到redis并设置过期时间
redis.boundValueOps(userId).set(token, Constants.TOKEN_EXPIRES_HOUR, TimeUnit.HOURS);
return model;
}
public boolean checkToken(TokenModel model) {
if (model == null) {
return false;
}
String token = redis.boundValueOps(model.getUserId()).get();
if (token == null || !token.equals(model.getToken())) {
return false;
}
//如果验证成功,说明此用户进行了一次有效操作,延长token的过期时间
redis.boundValueOps(model.getUserId()).expire(Constants.TOKEN_EXPIRES_HOUR, TimeUnit.HOURS);
return true;
}
public TokenModel getToken(String authentication) {
if (authentication == null || authentication.length() == 0) {
return null;
}
String[] param = authentication.split("_");
if (param.length != 2) {
return null;
}
//使用userId和源token简单拼接成的token,可以增加加密措施
String userId = param[0];
String token = param[1];
return new TokenModel(userId, token);
}
public void deleteToken(String userId) {
redis.delete(userId);
}
}
controller层进行登录
@PostMapping("/login")
public Object Login(@RequestBody Map<String, Object> reqMap) {
String userName = RequestUtil.getMapString(reqMap.get("user_name").toString());
String passWord = RequestUtil.getMapString(reqMap.get("pass_word").toString());
//判断用户名是否存在
User user = userServer.getUser(userName);
if (user == null) {
return ResultUtil.fail(ResultEnum.LOGIN_FAIL);
}
//获取数据库中的密码,与输入的密码加密后比对
String dbPassword = user.getPassWord();
String equalPassword = MD5Util.inputPassToDbPass(passWord);
if (!equalPassword.equals(dbPassword)) {
return ResultUtil.fail(ResultEnum.LOGIN_FAIL_PASS);
}
//生成一个token,保存用户登录状态
TokenModel model = tokenManager.createToken(user.getId());
return ResultUtil.ok(model);
}
商城项目地址
https://github.com/627886474/sneaker
欢迎start,如有不足,还请指教。