REST即表述性状态传递(英文:Representational State Transfer,简称REST)是Roy Fielding博士在2000年他的博士论文中提出来的一种软件架构风格。它是一种针对网络应用的设计和开发方式,可以降低开发的复杂性,提高系统的可伸缩性。
1.基于Session登录验证(有会话状态)
用户发出登录请求,带着用户名和密码到服务器验证,服务器验证成功就将用户信息写入到Session中。后续用户再请求就可以查看Session中有没有登录信息,有就允许登入或者继续操作,没有就返回到登入界面
2.基于Token登录验证(无会话状态)
用户发出登录请求,带着用户名和密码到服务器经行验证,服务器验证成功就在后台生成一个令牌返回给客户端,客户端把这个令牌储存起来(浏览器可以存储到Cookie中,服务端可以存放到Session,数据库,Redis中),redis可以设置存储令牌的有效期。后续的每次操作客户端都需要带着令牌发出请求,服务器会对令牌进行检测,(比如说)成功允许继续操作,时报就返回到登录界面。
令牌管理:生成令牌、有效期管理、销毁令牌。
令牌号:可以使用JWT生成令牌,使用用户ID生成令牌
有效期:使用Redis key有效期设置(每次操作完了都会更新延长有效时间)
销毁令牌:清空Redis
令牌存储:客户端(Cookie)、服务端(Redis)
Cookie的存取操作(js插件)
Redis存取(RedisTemplate操作)
具体代码
1.令牌类
生成Token
使用UUID算法工具类随机生成一个不重复的随机值,再加上userid组合成Token字符串,存入Redis并返回
检查Token
解析出userid和uuid(首先保证非空,按_拆分,判断格式),根据Redis进行检查(去除redis中的Token和key比较),再更新Token时间,返回检测结果。
销毁Token
检查一个token是否正确,解析出userid和uuid(首先保证非空,按_拆分,判断格式)根据Redis进行检查(去除redis中的Token和key比较),再删除Token。
@Component
public class TokenManager {
@Autowired
private RedisTemplate<Object, Object> redisTemplate;
/**
* 生成一个令牌
* @param userId 用户ID
* @return 返回令牌
*/
public String createToken(int userId){
//生成token
UUID uuid = UUID.randomUUID();
String token = userId+"_"
+uuid.toString().replaceAll("-", "");
//将token存入redis
String key = userId+"_token";
redisTemplate.opsForValue().set(key, token,
Constants.TOKEN_EXPIRE_HOUR, TimeUnit.HOURS);
return token;
}
/**
* 检查token是否正确
* @param token 令牌
* @return true正确;false失败
*/
public boolean checkToken(String token){
//解析出userId和uuid
if(token==null || "".equals(token)){
return false;
}
String[] arr1 = token.split("_");
if(arr1.length != 2){
return false;
}
//根据redis进行检查
String key = arr1[0]+"_token";
String r_token = (String)redisTemplate.opsForValue().get(key);
if(r_token==null){
return false;
}
if(!token.equals(r_token)){
return false;
}
//返回检测结果,更新token时间
redisTemplate.opsForValue().set(key, token,
Constants.TOKEN_EXPIRE_HOUR, TimeUnit.HOURS);
return true;
}
/**
* 注销Token
* @param token 令牌
* @return true正确;false失败
*/
public boolean clearToken(String token){
//解析出userId和uuid
if(token==null || "".equals(token)){
return false;
}
String[] arr1 = token.split("_");
if(arr1.length != 2){
return false;
}
//根据redis进行检查
String key = arr1[0]+"_token";
String r_token = (String)redisTemplate.opsForValue().get(key);
if(r_token==null){
return false;
}
//注销token
redisTemplate.delete(key);
return true;
}
public static void main(String[] args){
TokenManager manager = new TokenManager();
System.out.println(manager.createToken(1));
}
}
测试类
@RunWith(SpringRunner.class)
@SpringBootTest(classes={BootApplication.class})
public class TestTokenManager {
@Autowired
private TokenManager manager;
@Test
public void test1(){
String token = manager.createToken(1);
System.out.println(token);
}
@Test
public void test2(){
boolean ok = manager.checkToken("1_add8f21134984c3fab898f9451acaa03");
System.out.println(ok);
}
}
public interface UserService {
public ResponseEntity checkLogin(String username,String password);
}
@Service
public class UserServiceImpl implements UserService{
@Autowired
private UserMapper userMapper;
@Autowired
private TokenManager tokenManager;
@Override
public ResponseEntity checkLogin(String username, String password) {
ResponseEntity response = new ResponseEntity();
//检测用户
User user = userMapper.selectByName(username);
if(user == null){
response.setStatus(2);
response.setMsg("用户错误");
} else if(!user.getPassword().equals(password)){
//检测密码
response.setStatus(3);
response.setMsg("密码错误");
}else{
//成功
response.setStatus(1);
response.setMsg("登录成功");
String token = tokenManager.createToken(user.getId());
response.setData(token);
}
return response;
}
}
UserController
@RestController
public class UserController {
@Autowired
private UserService userService;
@RequestMapping(value="/user/token",method=RequestMethod.POST)
public ResponseEntity login(String username,String password){
return userService.checkLogin(username, password);
}
}
单体测试
@RunWith(SpringRunner.class)
@SpringBootTest(classes={BootApplication.class})
@WebAppConfiguration//启动tomcat
public class TestUserController {
@Autowired
private UserController userController;
//发送MVC请求,获取响应结果
MockMvc mockMvc = null;
@Before//每次执行@Test方法前都会调用init方法
public void init(){
mockMvc = MockMvcBuilders.standaloneSetup(userController).build();
}
@Test//登录成功
public void test1() throws Exception{
//发送一个post请求
MockHttpServletRequestBuilder postRequest =
MockMvcRequestBuilders.post("/user/token")
.param("username", "scott")
.param("password", "123456");
//执行请求
MvcResult result = mockMvc.perform(postRequest).andReturn();
//获取返回结果内容
String body = result.getResponse().getContentAsString();
System.out.println(body);
}
@Test//用户错误
public void test2() throws Exception{
//发送一个post请求
MockHttpServletRequestBuilder postRequest =
MockMvcRequestBuilders.post("/user/token")
.param("username", "scott1")
.param("password", "123456");
//执行请求
MvcResult result = mockMvc.perform(postRequest).andReturn();
//获取返回结果内容
String body = result.getResponse().getContentAsString();
System.out.println(body);
}
@Test//密码错误
public void test3() throws Exception{
//发送一个post请求
MockHttpServletRequestBuilder postRequest =
MockMvcRequestBuilders.post("/user/token")
.param("username", "scott")
.param("password", "12345");
//执行请求
MvcResult result = mockMvc.perform(postRequest).andReturn();
//获取返回结果内容
String body = result.getResponse().getContentAsString();
System.out.println(body);
}
}