token机制原理图
下面是实现token机制的代码
项目结构图(springboot项目)
pom.xml文件
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.1.0</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.31</version>
</dependency>
<dependency>
<groupId>nl.bitwalker</groupId>
<artifactId>UserAgentUtils</artifactId>
<version>1.2.4</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.6</version>
</dependency>
redis.properties文件
#访问地址
redis.host=192.168.126.130
#访问端口
redis.port=6379
#注意,如果没有password,此处不设置值,但这一项要保留
redis.pass=123456
#超时时间
redis.timeout=60
#使用哪个数据库
redis.default.db=0
#最大空闲时间,数据库连接的最大空闲时间。超过空闲时间,数据库连接将被标记为不可用,然后被释放。设为0表示无限制。
redis.maxIdle=300
#连接池的最大数据库连接数。设为0表示无限制
redis.maxActive=600
#最大建立连接等待时间。如果超过此时间将接到异常。设为-1表示无限制。
redis.maxWait=1000
#在borrow一个jedis实例时,是否提前进行validate操作;如果为true,则得到的jedis实例均是可用的;
redis.testOnBorrow=true
applicationContext.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.3.xsd">
<context:property-placeholder location="classpath:redis.properties"/>
<!-- 配置JedisPoolConfig实例 -->
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxIdle" value="${redis.maxIdle}" />
<property name="maxActive" value="${redis.maxActive}" />
<property name="maxWait" value="${redis.maxWait}" />
<property name="testOnBorrow" value="${redis.testOnBorrow}" />
</bean>
<!-- 配置JedisPool实例 -->
<bean id="jedisPool" class="redis.clients.jedis.JedisPool" destroy-method="destroy">
<constructor-arg ref="jedisPoolConfig" />
<constructor-arg value="${redis.host}" />
<constructor-arg value="${redis.port}" />
<constructor-arg value="${redis.timeout}" />
<constructor-arg value="${redis.pass}" />
<constructor-arg value="${redis.default.db}" />
</bean>
<bean id="redisUtil" class="com.fengqing.af.util.RedisUtil">
<property name="jedisPool" ref="jedisPool" />
</bean>
</beans>
User.java
package com.fengqing.af.bean;
public class User{
private Integer id;
private String username;
private String password;
public User() {
}
public User(Integer id, String username, String password) {
this.id = id;
this.username = username;
this.password = password;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
Dto.java
package com.fengqing.af.bean;
public class Dto {
private String token;
//token创建时间
private Long tokenCreatedTime;
//失效时间
private Long tokenExpiryTime;
private String isLogin;
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public Long getTokenCreatedTime() {
return tokenCreatedTime;
}
public void setTokenCreatedTime(Long tokenCreatedTime) {
this.tokenCreatedTime = tokenCreatedTime;
}
public Long getTokenExpiryTime() {
return tokenExpiryTime;
}
public void setTokenExpiryTime(Long tokenExpiryTime) {
this.tokenExpiryTime = tokenExpiryTime;
}
public String getIsLogin() {
return isLogin;
}
public void setIsLogin(String isLogin) {
this.isLogin = isLogin;
}
}
RedisUtil.java
package com.fengqing.af.util;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
public class RedisUtil {
private JedisPool jedisPool;
public void setJedisPool(JedisPool jedisPool) {
this.jedisPool = jedisPool;
}
//添加
public void set(String key, String value){
Jedis jedis = this.jedisPool.getResource();
jedis.set(key, value);
this.jedisPool.returnResource(jedis);
}
//添加,带超时时间
public void setex(String key, int seconds, String value){
Jedis jedis = this.jedisPool.getResource();
jedis.setex(key, seconds, value);
this.jedisPool.returnResource(jedis);
}
//获取
public String get(String key){
Jedis jedis = this.jedisPool.getResource();
String value = jedis.get(key);
this.jedisPool.returnResource(jedis);
return value;
}
//查看某个键是否存在
public boolean exists(String key){
Jedis jedis = this.jedisPool.getResource();
Boolean exists = jedis.exists(key);
this.jedisPool.returnResource(jedis);
return exists;
}
//查看超时时间
public Long ttl(String key){
Jedis jedis = this.jedisPool.getResource();
Long ttl = jedis.ttl(key);
this.jedisPool.returnResource(jedis);
return ttl;
}
//删除
public void del(String key){
Jedis jedis = this.jedisPool.getResource();
jedis.del(key);
this.jedisPool.returnResource(jedis);
}
}
TokenService.java
package com.fengqing.af.service;
import com.alibaba.fastjson.JSONObject;
import com.fengqing.af.bean.User;
import com.fengqing.af.util.RedisUtil;
import nl.bitwalker.useragentutils.UserAgent;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.stereotype.Service;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Random;
@Service("tokenService")
public class TokenService {
private RedisUtil redisUtil;
public TokenService(){
init();
}
private void init() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
redisUtil = (RedisUtil)applicationContext.getBean("redisUtil");
}
//生成token(格式为token:设备-加密的用户名-时间-六位随机数)
public String generateToken(String userAgent, String username){
StringBuilder token = new StringBuilder();
//加token:
token.append("token:");
//加设备
UserAgent userAgent1 = UserAgent.parseUserAgentString(userAgent);
if (userAgent1.getOperatingSystem().isMobileDevice()){
token.append("MOBILE-");
} else {
token.append("PC-");
}
//加加密的用户名
token.append(DigestUtils.md5Hex(username) + "-");
//加时间
token.append(new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date()) + "-");
//加六位随机数111111-999999
token.append(new Random().nextInt((999999 - 111111 + 1)) + 111111);
System.out.println("token=>" + token.toString());
return token.toString();
}
//保存token
public void saveToken(String token, User user){
//如果是PC,那么token保存两个小时;如果是MOBILE
if (token.startsWith("token:PC")) {
redisUtil.setex(token,2*60*60, JSONObject.toJSONString(user));
} else {
redisUtil.set(token, JSONObject.toJSONString(user));
}
}
}
UserService.java
package com.fengqing.af.service;
import com.fengqing.af.bean.User;
import org.springframework.stereotype.Service;
@Service("userService")
public class UserService {
public User login(String username, String password){
//假设数据库有个用户用户名为fengqing,密码为123456
if ("fengqing".equals(username) && "123456".equals(password)){
return new User(1, "fengqing", "123456");
} else {
return null;
}
}
}
UserController.java
package com.fengqing.af.controller;
import com.alibaba.fastjson.JSONObject;
import com.fengqing.af.bean.Dto;
import com.fengqing.af.bean.User;
import com.fengqing.af.service.TokenService;
import com.fengqing.af.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
@Controller
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@Autowired
private TokenService tokenService;
@RequestMapping("/login")
@ResponseBody
public String login(String username, String password, HttpServletRequest request){
Dto dto = new Dto();
User user = this.userService.login(username, password);
if (user != null) {//登录成功生成token并保存token
String userAgent = request.getHeader("user-agent");
String token = this.tokenService.generateToken(userAgent, username);
this.tokenService.saveToken(token, user);
dto.setIsLogin("true");
dto.setToken(token);
dto.setTokenCreatedTime(System.currentTimeMillis());
dto.setTokenExpiryTime(System.currentTimeMillis() + 2*60*60*1000);
} else {
dto.setIsLogin("false");
}
return JSONObject.toJSONString(dto);
}
}