Tocken验证登录

原理:
客户端第一次登陆系统,用户名密码验证通过生成tocken存到redis,并且设置过期时间。客户端请求其他功能的时候带着tocken访问,每次访问都重置tocken过期时间。退出登录,删除redis中的tocken

实现过程:springboot项目
pom.xml
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-redis</artifactId>
    <version>1.4.1.RELEASE</version>
</dependency>


-------------------登录controller

    @Autowired
    private TokenHelper tokenHelper;

    @NoneAuth
    @GetMapping("/login")
    public Object login(String username, String password,Integer id) {
        //这里暂时用username,password,id 代替User 实体类
        if(username == null || !password.equals("123")) {
            return JsonData.buildError(HttpStatusEnum.NOT_FOUND.getCode(), MessageConstant.USERNAME_OR_PASSWORD_ERROR);
        }
        //用户名密码验证通过后,生成token
        TokenModel model = tokenHelper.create(id);
        return JsonData.buildSuccess(model);
    }

    /**退出登录
     * tocken 删除用户id
     * @param request
     * @return
     */
    @DeleteMapping
    public Object logout(HttpServletRequest request) {
        Integer userId = (Integer) request.getAttribute(NormalConstant.CURRENT_USER_ID);
        if(userId != null) {
            tokenHelper.delete(userId);
        }
        return JsonData.buildSuccess();
    }
}

------------------------tocken Model 存tocken信息,主要有用户id和tocken
public class TokenModel {
    private Integer userId;

    private String token;

    public TokenModel(Integer userId, String token) {
        this.userId = userId;
        this.token = token;
    }

    public Integer getUserId() {
        return userId;
    }

    public void setUserId(Integer userId) {
        this.userId = userId;
    }

    public String getToken() {
        return token;
    }

    public void setToken(String token) {
        this.token = token;
    }

    @Override
    public String toString() {
        return "TokenModel [userId=" + userId + ", token=" + token + "]";
    }

}


------处理tocken------------tockenhelper 对token在redis增删检查的接口

public interface TokenHelper {
    /**
     * 根据用户id生成token
     * @param id
     * @return
     */
    TokenModel create(Integer id);

    /**
     * 检查是否存在该token
     * @param model
     * @return
     */
    boolean check(TokenModel model);

    /**
     * 从前端返回的校验字符串中获取token
     * @param authStr
     * @return
     */
    TokenModel get(String authStr);

    /**
     * 根据用户id删除token
     * @param mode
     * @return
     */
    boolean delete(Integer id);
}


--------------------- tockenhelper的实现类

@Component
public class RedisTokenHelp implements TokenHelper {
    @Autowired
    private RedisClient redisClient;

    @Override
    public TokenModel create(Integer id) {
        String token = UUID.randomUUID().toString().replace("-", "");
        TokenModel mode = new TokenModel(id, token);
        redisClient.set(id == null ? null : String.valueOf(id), token, RedisClient.TOKEN_EXPIRES_SECOND);
        return mode;
    }

    @Override
    public boolean check(TokenModel model) {
        boolean result = false;
        if(model != null) {
            String userId = model.getUserId().toString();
            String token = model.getToken();
            String authenticatedToken = redisClient.get(userId);
            if(authenticatedToken != null && authenticatedToken.equals(token)) {
                redisClient.expire(userId, RedisClient.TOKEN_EXPIRES_SECOND);
                result = true;
            }
        }
        return result;
    }

    @Override
    public TokenModel get(String authStr) {
        TokenModel model = null;
        if(StringUtils.isNotEmpty(authStr)) {
            String[] modelArr = authStr.split("_");
            if(modelArr.length == 2) {
                int userId = Integer.parseInt(modelArr[0]);
                String token = modelArr[1];
                model = new TokenModel(userId, token);
            }
        }
        return model;
    }

    @Override
    public boolean delete(Integer id) {
        return redisClient.remove(id == null ? null : String.valueOf(id));
    }
}

----------------------redis工具类 设置过期时间,取值存值

@Component
public class RedisClient { //关于 redis tocken 过期时间处理
    public static final long TOKEN_EXPIRES_SECOND = 1800;//秒

    @Autowired
    private StringRedisTemplate redisTpl;

    /**
     * 向redis中设值
     * @param key 使用 a:b:id的形式在使用rdm进行查看redis情况时会看到分层文件夹的展示形式,便于管理
     * @param value
     * @return
     */
    public boolean set(String key, String value) {
        boolean result = false;
        try {
            redisTpl.opsForValue().set(key, value);
            result = true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }


    /**
     * 向redis中设置,同时设置过期时间
     * @param key
     * @param value
     * @param time
     * @return
     */
    public boolean set(String key, String value, long time) {
        boolean result = false;
        try {
            redisTpl.opsForValue().set(key, value);
            expire(key, time);
            result =  true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 获取redis中的值
     * @param key
     * @return
     */
    public String get(String key) {
        String result = null;
        try {
            result = redisTpl.opsForValue().get(key);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;

    }

    /**
     * 设置key的过期时间
     * @param key
     * @param time
     * @return
     */
    public boolean expire(String key, long time) {
        boolean result = false;
        try {
            if(time > 0) {
                redisTpl.expire(key, time, TimeUnit.SECONDS);
                result = true;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 根据key删除对应value
     * @param key
     * @return
     */
    public boolean remove(String key) {
        boolean result = false;
        try {
            redisTpl.delete(key);
            result = true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }
}

-----------------------客户端登录状态码

public enum HttpStatusEnum {
    SUCCESS(200, "请求成功"), BAD_REQUEST(400, "报文语法错误"), UNAUTHORIZED(401, "用户未认证"),
    FORBIDDEN(403, "拒绝访问"), NOT_FOUND(404, "资源不存在"), INTERNAL_SERVER_ERROR(500, "服务器错误");

    private int code;
    private String info;

    private HttpStatusEnum(int code, String info) {
        this.code = code;
        this.info = info;
    }

    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }

    public String getInfo() {
        return info;
    }

    public void setInfo(String info) {
        this.info = info;
    }

    public static HttpStatusEnum getInfoByCode(int code) {
        for(HttpStatusEnum status :values()) {
            if(status.getCode() == code) {
                return status;
            }
        }
        return null;
    }
}


---------------------自定义注解,controller加上此注解,拦截器直接通过
/**
 * 使用本注解的方法不会进行登录验证
 * @author baipengfei
 *
 */
@Documented
@Retention(RUNTIME)
@Target(METHOD)
public @interface NoneAuth {

}



------------------------自定义拦截器

//自定义拦截器
@Component
public class LoginInterceptor extends HandlerInterceptorAdapter {
    @Autowired
    private TokenHelper tokenHelper;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        System.out.println("这里是拦截器");
        // 如果不是映射到方法直接通过
        if (!(handler instanceof HandlerMethod)) {
            return true;
        }
        //如果被@NoneAuth注解代表不需要登录验证,直接通过
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        Method method = handlerMethod.getMethod();
        if(method.getAnnotation(NoneAuth.class) != null) return true;
        //token验证
        String authStr = request.getHeader(NormalConstant.AUTHORIZATION);
        TokenModel model = tokenHelper.get(authStr);

        //验证通过
        if(tokenHelper.check(model)) {
            request.setAttribute(NormalConstant.CURRENT_USER_ID, model.getUserId());
            return true;
        }
        //验证未通过
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json; charset=utf-8");
        response.getWriter().write(JsonUtils.obj2String(JsonData.buildError(401, "权限未认证")));
        return false;
    }
}



-------------------------注册拦截器,使拦截器生效
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {//注册拦截器,使它生效

    @Autowired
    LoginInterceptor loginInterceptor;
    public void addInterceptors(InterceptorRegistry registry) {
        //註冊TestInterceptor拦截器
        InterceptorRegistration registration = registry.addInterceptor(loginInterceptor);
        registration.addPathPatterns("/**");                    //所有路径都被拦截

    }
}


-------------------------------
此时登录之后用户信息就会存在redis格式为<key value> = <userid  tocken>返回给客户端格式为:
{
    "code": 200,
    "data": {
        "userId": 11123,
        "token": "55af99e971184acda806ae4d6186f716"
    },
    "msg": "请求成功"
}

前端界面验证tocken,header参数为 authStr 值为 userid_tocken 
例:
11123_55af99e971184acda806ae4d6186f716







你可能感兴趣的:(tocken验证登录)