写一个aop,实现用户身份验证和所传入参数判空
用于app端访问方法时的用户身份验证和所传入参数判空,还有登录用户超时限制,限制单个用户在多设备登录。
采用注解,每个用户分配一个token,存于redis中。
1.开启aop功能
在项目相关xml配置文件加入
<aop:aspectj-autoproxy proxy-target-class="true" />
2.定义注解
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) @Documented public @interface ValidUserAndParams {
//用户token String token() default "token";
//用户返回值 String resultCode() default "code";
//传入参数 String[] params() default {}; }
3.定义切面
@Component @Aspect public class ValidAspect { @Autowired private ValidUtil validUtil; @Around("execution(public Object packagename..*.*(..)) && @annotation(vup)") public Object userAndParamsCheck(ProceedingJoinPoint joinPoint,ValidUserAndParams vup) { return validkUtil.checkUser(joinPoint,vup); } }
ValidUtil.java
@Component public class ValidUtil { @Autowired private UserToken userToken; public Object checkUser(ProceedingJoinPoint joinPoint,ValidUserAndParams vup) { TwoTuple<Boolean,JSONObject> tokenInfo=null; try{ Object[] params=joinPoint.getArgs(); if(params[0] instanceof HttpServletRequest){ HttpServletRequest request=(HttpServletRequest)params[0]; tokenInfo=userToken.isValidToken(request,ac.token(),ac.resultCode()); if(!tokenInfo.first) return tokenInfo.second; for(String i : ac.params()){ if(null==request.getParameter(i)||"".equals(request.getParameter(i))){ tokenInfo.second.put("code", Code.paramValueInvalid); tokenInfo.second.put("msg","参数 "+i+" 不能为空"); return tokenInfo.second; } } } return joinPoint.proceed(); } catch (Throwable throwable) { throwable.printStackTrace(); } return tokenInfo.second; } }
UserToken.java
@Component public class UserToken { @Autowired private RedisExeUtil redisExeUtil; @Autowired private ConfigInfo configInfo; /** * 用户校验通过之后需要调用此方法来分配token * 1.如果该用户此前已经登录且token没有过期,就删除该未过期的token * 2.如果不存在token就生成该token码 * 3.生成token码之后就将token码保存 * @param userName * @return */ public String distributionToken(String userName){ //用户登录的时候,分配token的 String token=redisExeUtil.getMpVByK(CreateRedisKey.createUser2TokenKey(),userName); if(token!=null) //删除之前存在的映射关系 redisExeUtil.deletevalue(CreateRedisKey.createToken2User(token)); //删除之前记录的token //重新分配新的token 并记录token的信息 String newToken=HashFactory.hashKeyForDisk(System.currentTimeMillis()+userName); //计算该用户名的hash redisExeUtil.expire(CreateRedisKey.createToken2User(newToken), 1800, userName); //hash-->userName 保存了Hash到userName的映射关系 //记录user->token的信息 redisExeUtil.addMpKV(CreateRedisKey.createUser2TokenKey(),userName,newToken); return newToken; } /** * 1.可能已经过期,如果过期就返回响应的过期提示(可能用户在另外一台设备登录或已超时) * 2.没有过期,返回响应的用户名 * @param token * @return */ public JSONObject getUserNameByToken(String token){ JSONObject result=new JSONObject(); if(token==null || "".equals(token)){ result.put("code", Code.tokenIsError); result.put("msg","token为空"); //是否清空表中的数据 return result; } String userName=redisExeUtil.getStringVal(CreateRedisKey.createToken2User(token)); if(userName==null){ result.put("code", Code.tokenIsError); result.put("msg","登录超时,或者被其它设备登录"); //是否清空表中的数据 }else{ result.put("code", Code.success); result.put("msg","token验证通过"); result.put("name",userName);
//设置app端用户的登录失效时间为30分钟,单位为秒,-1表示不过期 redisExeUtil.expire(CreateRedisKey.createToken2User(token), 1800, userName); } return result; //过期了就没有了,重新登录就会分配新的token码 } public JSONObject getUserNameByToken(HttpServletRequest request,String tokenName){ String token=request.getParameter(tokenName); return getUserNameByToken(token); } public boolean isValidToken(JSONObject tokenObj,String code){ return tokenObj.getInteger(code)==Code.success; } public TwoTuple<Boolean,JSONObject> isValidToken(HttpServletRequest request,String tokenName,String code){ JSONObject tokenObj=getUserNameByToken(request,tokenName); return TupleUtil.tuple(isValidToken(tokenObj,code),tokenObj); } }
写了个泛型二元组TwoTuple.java
public class TwoTuple<X,Y> { public final X first; public final Y second; public TwoTuple(X x,Y y){ this.first=x; this.second=y; } }
HashFactory.java
public class HashFactory { public static String hashKeyForDisk(String key) { String cacheKey; try { final MessageDigest mDigest = MessageDigest.getInstance("MD5"); mDigest.update(key.getBytes()); cacheKey = bytesToHexString(mDigest.digest()); } catch (NoSuchAlgorithmException e) { cacheKey = String.valueOf(key.hashCode()); } return cacheKey; } private static String bytesToHexString(byte[] bytes) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < bytes.length; i++) { String hex = Integer.toHexString(0xFF & bytes[i]); if (hex.length() == 1) { sb.append('0'); } sb.append(hex); } return sb.toString(); } }
redis的命令封装类RedisExeUtil.java
@Component public class RedisExeUtil { @Autowired private RedisManager redisManager; @Autowired private CheckUtil check; //---------------------------------------String类型数据------------------------------------------------/** * 作用:将传入的k,v写入到redis * redis管理对象 * @param key : 存入redis的key * * @param value : 支持的类型为 String Number Object * 如果为Object存储的就为其转化为JSON字符串后的值 */ public String writeString(String key,Object value){ return (String)redisManager.execute(new RedisDaoIf() { @Override public Object execute(Jedis jedis, Object... params) { String key = (String) params[0]; Object value = params[1]; if(value instanceof Number || value instanceof String){ jedis.set(key, value+""); }else{ jedis.set(key,JSONObject.fromObject(value).toString()); } return key; } },key,value); } /** * 作用:根据传入的key获取该key对应的String值 * @param key * 需要获取的key值 * @return */ public String getStringVal(String key){ return (String)redisManager.execute(new RedisDaoIf() { @Override public Object execute(Jedis jedis, Object... params) { // Pipeline pipeline=jedis.pipelined(); String key = (String) params[0]; return jedis.get(key); } },key); } //---------------------------------------List类型数据------------------------------------------------ /** * 作用:使用增量的方式向redis中追加List值 * 使用方法见 writeList * @param key * @param value * @return */ public int writeListIncrement(String key,Collection<? extends Object> value){ return writeList(key,value,true); } /** * 作用:向redis中写入一个全新的List,也就是使用非增量写 * 使用方法见 writeList * @param key * @param value * @return */ public int writeNewList(String key,Collection<? extends Object> value){ return writeList(key,value,false); } /** * 作用:用于将集合写入到redis中 * @param key * @param value * @param Isadd : 如果此处为真则表明是向集合中追加,否则将删除该key原有的值,然后在生成该key * 集合支持的类型有String,Number,如果为其它类型将会存储 ClassName@Hash 信息 * @return * 存入到redis中集合的大小 */ public int writeList(String key,Collection<? extends Object> value,boolean Isadd){ return (Integer)redisManager.execute(new RedisDaoIf() { @Override public Object execute(Jedis jedis, Object... params) { Pipeline pipeline=jedis.pipelined(); String key=(String)params[0]; Object[] tmp=((Collection)params[1]).toArray(); String [] a=new String[tmp.length]; for(int i=0;i<tmp.length;i++){ a[i]=tmp[i]+""; } boolean Isadd=(boolean)params[2]; if(!Isadd){ if(jedis.exists(key)) pipeline.del(key); } pipeline.lpush(key,a); return a.length; } },key,value,Isadd); } /** * 作用:通过key获取该key下面的List结构的数据 * * redis管理对象 * @param key * key值 * @return */ public List<String> getListString(String key){ return ( List<String>)redisManager.execute(new RedisDaoIf() { @Override public Object execute(Jedis jedis, Object... params) { // Pipeline pipeline=jedis.pipelined(); String key = (String) params[0]; return jedis.lrange(key,0,-1); } },key); } public List<String> getListFirstEle(String key){ return ( List<String>)redisManager.execute(new RedisDaoIf() { @Override public Object execute(Jedis jedis, Object... params) { // Pipeline pipeline=jedis.pipelined(); String key = (String) params[0]; return jedis.lrange(key,0,0); } },key); } //---------------------------------------Map类型数据 redis中关于Map的操作函数都是以h开头------------------------------------------------ /** * 作用:根据key得到该key对应的Map对象 * @param key * @return */ public Map<String,String> getMapString(String key){ return (Map<String,String>)redisManager.execute(new RedisDaoIf() { @Override public Object execute(Jedis jedis, Object... params) { // Pipeline pipeline=jedis.pipelined(); String key = (String) params[0]; return jedis.hgetAll(key); } },key); } /** * 作用:用于将Map类型的数据写到Redis中 返回值为插入到redis中Map的size * Map<ObjectKey,ObjectValue> 其中ObjectKey支持的数据类型有 String Number * ObjectValue中支持的数据类型有 String Number Collection * 如果ObjectValue中的数据不为上面三种而为Object,将会被转化为JSON对象字符串 * @param key * @param value */ public Integer writeMap(String key,Map<? extends Object,? extends Object> value){ Integer result=(Integer)redisManager.execute(new RedisDaoIf() { @Override public Object execute(Jedis jedis, Object... params) { Pipeline pipeline=jedis.pipelined(); Object key = params[0]; Map<Object, Object> value = (Map<Object, Object>) params[1]; Map<String, String> tmp = new HashMap<String, String>(); for (Object o : value.keySet()) { Object vo=value.get(o); if(vo instanceof String || vo instanceof Number){ tmp.put(o + "", vo+ ""); }else if(vo instanceof Collection) { tmp.put(o + "", JSONArray.fromObject(vo).toString()); }else{ tmp.put(o + "", JSONObject.fromObject(vo).toString()); } } pipeline.hmset(key + "", tmp); return value.size(); } }, key, value); return result; } /** * 作用:用于获取map结构中的某个值 * @param redisKey : redis的key值 * @param mapKey :map中的key值 * @return */ public String getMpVByK(String redisKey,String mapKey){ return (String)redisManager.execute(new RedisDaoIf() { @Override public Object execute(Jedis jedis, Object... params) { return jedis.hget((String) params[0], (String) params[1]); } },redisKey,mapKey); } /** * 作用:用于删除Map结构中的某个KV对 * @param redisKey * @param mapKey * @return */ public String delMpKVByK(String redisKey,String mapKey){ return (String)redisManager.execute(new RedisDaoIf() { @Override public Object execute(Jedis jedis, Object... params) { Pipeline pipeline=jedis.pipelined(); pipeline.hdel((String) params[0], (String) params[1]); return (String)params[1]; } },redisKey,mapKey); } /** * 作用:向redis中的Map结构中增加一个KV对 * Hset 命令用于为哈希表中的字段赋值 。 * 如果哈希表不存在,一个新的哈希表被创建并进行HSET操作。 * 如果字段已经存在于哈希表中,旧值将被覆盖。 * @param redisKey : redis中的key * @param mapKey : 需要存储的map的key * @param value : 需要存储的map的v * @return : 返回已经插入到map中的key值 */ public String addMpKV(String redisKey,String mapKey,Object value){ return (String)redisManager.execute(new RedisDaoIf() { @Override public Object execute(Jedis jedis, Object... params) { Pipeline pipeline=jedis.pipelined(); Object o =params[2]; if(o instanceof String || o instanceof Number){ pipeline.hset((String)params[0],(String)params[1],o+""); }else if(o instanceof Collection){ pipeline.hset((String) params[0], (String) params[1], JSONArray.fromObject(o).toString()); }else{ pipeline.hset((String)params[0],(String)params[1],JSONObject.fromObject(o).toString()); } return (String)params[0]; } },redisKey,mapKey,value); } /** * 作用:获取某个Map结构中的全部key值 * @param key * @return */ public Set<String> getMapKs(String key){ return (Set<String>)redisManager.execute(new RedisDaoIf() { @Override public Object execute(Jedis jedis, Object... params) { return jedis.hkeys((String)params[0]); } },key); } /** * 作用:用于获取某个Map结构中的全部value值 * @param key * @return */ public List<String> getMapVs(String key){ return (List<String>)redisManager.execute(new RedisDaoIf() { @Override public Object execute(Jedis jedis, Object... params) { return jedis.hvals((String)params[0]); } },key); } //---------------------------------------删除key值------------------------------------------------ /** * 作用:在redis中删除不需要的key * redis管理对象 * @param keys :需要删除的key ,可以传入多个Key * @return int : 实际删除的key值的数量,注意此数量并不准确,切勿用作对数量要求比较高的判断 */ public int deletevalue(String... keys){ String[] a= (String[]) keys; return deleteKey(a); } /** * 作用:用于批量删除redis中的key * @param keys :此集合为所有需要删除的Key的集合 * @return */ public int deletevalue(Collection<String> keys){ if(null == keys)) return 0; String[] strs=new String[keys.size()]; String[] a=keys.toArray(strs); //String[] a= (String[]) keys.toArray(); return deleteKey(a); } /** * 作用:用于删除redis不需要的Key * @param keys :为需要删除的key的数组 * @return */ private int deleteKey(String[] keys){ return (Integer)redisManager.execute(new RedisDaoIf() { @Override public Object execute(Jedis jedis, Object... params) { Pipeline pipeline=jedis.pipelined(); String[] a= (String[]) params; pipeline.del(a); return a.length; } },keys); } /** * 作用:用于判断某个key是否存在 * @param key 被判断的key值 * @return */ public boolean IskeyExist(String key){ return (boolean)redisManager.execute(new RedisDaoIf() { @Override public Object execute(Jedis jedis, Object... params) { String key=(String)params[0]; return jedis.exists(key); } },key); } /** * 作用:用于修改redis中key的值 * @param oldKey :存在于redis中的key值 * @param newKey :需要修改成的key值 * @return */ public String renameKey(String oldKey,String newKey){ return (String)redisManager.execute(new RedisDaoIf() { @Override public Object execute(Jedis jedis, Object... params) { Pipeline pipeline=jedis.pipelined(); pipeline.rename((String)params[0],(String)params[1]); return params[1]; } },oldKey,newKey); } /** * 设置redis中的key value 同时设置该key的过期时间 * @param key * @param second * @param value * @return */ public boolean expire(String key,int second,String value){ return (boolean)redisManager.execute(new RedisDaoIf(){ @Override public Object execute(Jedis jedis, Object... params) { jedis.setex((String)params[0],(int)params[1],(String)params[2]); return true; } },key,second,value); } }
redis的配置类RedisManger.java
public class RedisManager { private String hostname; private int port; private String password; private int dbid; private String clientName; private int timeout; private JedisPool pool; //可用连接实例的最大数目,默认值为8; //如果赋值为-1,则表示不限制;如果pool已经分配了maxActive个jedis实例,则此时pool的状态为exhausted(耗尽)。 private static int MAX_ACTIVE = 1024; //控制一个pool最多有多少个状态为idle(空闲的)的jedis实例,默认值也是8。 private static int MAX_IDLE = 200; //等待可用连接的最大时间,单位毫秒,默认值为-1,表示永不超时。如果超过等待时间,则直接抛出JedisConnectionException; private static int MAX_WAIT = 10000; //在borrow一个jedis实例时,是否提前进行validate操作;如果为true,则得到的jedis实例均是可用的; private static boolean TEST_ON_BORROW = true; /** * 初始化redis的连接池 * 注意本方法只有在系统启动的时候调用,其它时候禁止调用 */ public void init(){ if(pool==null){ synchronized (this) { if(pool==null){ JedisPoolConfig config = new JedisPoolConfig(); config.setMaxTotal(MAX_ACTIVE); config.setMaxIdle(MAX_IDLE); config.setMaxWaitMillis(MAX_WAIT); config.setTestOnBorrow(TEST_ON_BORROW); //在将连接放回池中前,自动检验连接是否有效 config.setTestOnReturn(true); //自动测试池中的空闲连接是否都是可用连接 config.setTestWhileIdle(true); pool = new JedisPool(config,this.hostname, this.port, this.timeout, this.password, this.dbid, this.clientName); } } } } public JedisPool getJedisPool(){ return pool; } public Object execute(RedisDaoIf dao, Object ... params) { Jedis jedis = this.pool.getResource(); boolean hasExcep = false; try { try { return dao.execute(jedis, params); } catch (JedisException ex) { hasExcep = true; throw ex; } } finally { if (hasExcep) { this.pool.returnBrokenResource(jedis); } else { this.pool.returnResource(jedis); } } } public String getHostname() { return hostname; } public void setHostname(String hostname) { this.hostname = hostname; } public int getPort() { return port; } public void setPort(int port) { this.port = port; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public int getDbid() { return dbid; } public void setDbid(int dbid) { this.dbid = dbid; } public String getClientName() { return clientName; } public void setClientName(String clientName) { this.clientName = clientName; } public int getTimeout() { return timeout; } public void setTimeout(int timeout) { this.timeout = timeout; } public JedisPool getPool() { return pool; } public void setPool(JedisPool pool) { this.pool = pool; } }
具体信息位于xml文件
<bean id="redisManager" class="huayuutil.redis.RedisManager" init-method="init"> <!-- redis的连接地址 --> <property name="hostname" value="127.0.0.1" /> <!-- redis的端口 --> <property name="port" value="6379"/> <!-- redis的密码 --> <property name="password" value="xxxxxx"/> <!-- redis的连接库 --> <property name="dbid" value="1"/> <!-- 指定redis客户端的名称 --> <property name="clientName" value="redis"/> <!-- 指定redis的超时时间 --> <property name="timeout" value="10000"/>
</bean>
以上为用户登录过之后的校验。以下为某用户的初次登录分配token。