html登录状态验证,Token验证登录状态的简单实现

设计思路

用户发出登录请求,带着用户名和密码到服务器经行验证,服务器验证成功就在后台生成一个token返回给客户端

客户端将token存储到cookie中,服务端将token存储到redis中,可以设置存储token的有效期。

后续客户端的每次请求资源都必须携带token,这里放在请求头中,服务端接收到请求首先校验是否携带token,以及token是否和redis中的匹配,若不存在或不匹配直接拦截返回错误信息(如未认证)。

token管理:生成、校验、解析、删除

token:这里使用userId_UUID的形式

有效期:使用Redis key有效期设置(每次操作完了都会更新延长有效时间)

销毁token:删除Redis中key为userId的内容

token存储:客户端(Cookie)、服务端(Redis)

Cookie的存取操作(jquery.cookie插件)

Redis存取(StringRedisTemplate)

实现

(本demo只是个小例子,还有很多不足,用于实际环境还需要根据实际考虑更多的问题)

【Redis操作类】

package com.bpf.tokenAuth.utils;

import java.util.concurrent.TimeUnit;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.data.redis.core.StringRedisTemplate;

import org.springframework.stereotype.Component;

@Component

public class RedisClient {

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;

}

}

【Token管理类】

package com.bpf.tokenAuth.utils.token;

import java.util.UUID;

import org.apache.commons.lang3.StringUtils;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Component;

import com.bpf.tokenAuth.utils.RedisClient;

@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));

}

}

【拦截器逻辑】

package com.bpf.tokenAuth.interceptor;

import java.lang.reflect.Method;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Component;

import org.springframework.web.method.HandlerMethod;

import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import com.bpf.tokenAuth.annotation.NoneAuth;

import com.bpf.tokenAuth.constant.NormalConstant;

import com.bpf.tokenAuth.entity.JsonData;

import com.bpf.tokenAuth.utils.JsonUtils;

import com.bpf.tokenAuth.utils.token.TokenHelper;

import com.bpf.tokenAuth.utils.token.TokenModel;

@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(11);

// 如果不是映射到方法直接通过

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;

}

}

【登录逻辑】

package com.bpf.tokenAuth.controller;

import javax.servlet.http.HttpServletRequest;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.web.bind.annotation.DeleteMapping;

import org.springframework.web.bind.annotation.GetMapping;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RestController;

import com.bpf.tokenAuth.annotation.NoneAuth;

import com.bpf.tokenAuth.constant.MessageConstant;

import com.bpf.tokenAuth.constant.NormalConstant;

import com.bpf.tokenAuth.entity.JsonData;

import com.bpf.tokenAuth.entity.User;

import com.bpf.tokenAuth.enums.HttpStatusEnum;

import com.bpf.tokenAuth.mapper.UserMapper;

import com.bpf.tokenAuth.utils.token.TokenHelper;

import com.bpf.tokenAuth.utils.token.TokenModel;

@RestController

@RequestMapping("/token")

public class TokenController {

@Autowired

private UserMapper userMapper;

@Autowired

private TokenHelper tokenHelper;

@NoneAuth

@GetMapping

public Object login(String username, String password) {

User user = userMapper.findByName(username);

if(user == null || !user.getPassword().equals(password)) {

return JsonData.buildError(HttpStatusEnum.NOT_FOUND.getCode(), MessageConstant.USERNAME_OR_PASSWORD_ERROR);

}

//用户名密码验证通过后,生成token

TokenModel model = tokenHelper.create(user.getId());

return JsonData.buildSuccess(model);

}

@DeleteMapping

public Object logout(HttpServletRequest request) {

Integer userId = (Integer) request.getAttribute(NormalConstant.CURRENT_USER_ID);

if(userId != null) {

tokenHelper.delete(userId);

}

return JsonData.buildSuccess();

}

}

测试

【login.html】

25402229376e

Login

function login(){

$.ajax({

url: "/tokenAuth/token",

dataType: "json",

data: {'username':$("#username").val(), 'password':$("#password").val()},

type:"GET",

success:function(res){

console.log(res);

if(res.code == 200){

var authStr = res.data.userId + "_" + res.data.token;

//把生成的token放在cookie中

$.cookie("authStr", authStr);

window.location.href = "index.html";

}else alert(res.msg);

}

});

}

【index.html】

25402229376e

Index

function get(){

$.ajax({

url: "/tokenAuth/user/bpf",

dataType: "json",

type:"GET",

beforeSend: function(request) {

//将cookie中的token信息放于请求头中

request.setRequestHeader("authStr", $.cookie('authStr'));

},

success:function(res){

console.log(res);

}

});

}

function logout(){

$.ajax({

url: "/tokenAuth/token",

dataType: "json",

type:"DELETE",

beforeSend: function(request) {

//将cookie中的token信息放于请求头中

request.setRequestHeader("authStr", $.cookie('authStr'));

},

success:function(res){

console.log(res);

}

});

}

测试环境中两个页面login.html和index.html均当做静态资源处理

【未登录状态】

点击get按钮获取数据,由于没有携带token导致认证失败

25402229376e

【登录状态】

登录成功并跳转到index页面,并且生成cookie,这里没有设置cookie有效期,默认关闭浏览器失效

25402229376e

再次点击get按钮请求数据,请求成功

25402229376e

点击logout按钮销毁登录状态,然后再次请求数据

25402229376e

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值