精华内容
下载资源
问答
  • 不管是客户端接口还是网页H5接口,一般我们都需要登录验证,即要求所有的接口访问都必须在登录之后,以确认身份,防止非法调用。一般的流程都是登录的时候返回一个代表此登录的token,以后所有接口都带上此token,在...

    详情参见: https://gitee.com/xxssyyyyssxx/token

    不管是客户端接口还是网页H5接口,一般我们都需要登录验证,即要求所有的接口访问都必须在登录之后,以确认身份,防止非法调用。一般的流程都是登录的时候返回一个代表此登录的token,以后所有接口都带上此token,在所有接口调用之前拦截验证,一般都是通过AOP或者一个Filter、拦截器来实现。而退出的时候调用接口将此token删除即可。一般地,为了对接口侵入最小,能做到统一处理,可以将此token放在header中。token一般都会设置一个有效期,过期了直接提示调用者需要登录以控制条转到登录页面引导登录。

    服务端设计:

    /**
     * token管理器
     * @author xiongshiyan
     */
    public interface TokenManager<M> {
        /**
         * 生成token
         * @param m 实体
         * @return token值
         */
        String createToken(M m);
    
        /**
         * 根据token获取
         * @param token token
         * @return 根据token获取的实体
         */
        M findByToken(String token);
    
        /**
         * 更新token的过期
         * @param token token
         */
        void updateExpires(String token);
    
        /**
         * 删除
         * @param token token
         * @return 删除是否成功
         */
        boolean deleteToken(String token);
    
        /**
         * 产生token
         * @param m 实体
         * @return token
         */
        String getToken(M m);
    
    
        /**
         * 踢人
         * @param m 实体
         * @param newToken 新token
         * @param doMore 还要做的事情
         */
        default void kickingOld(M m, String newToken, Runnable doMore){
            kickingOld(m , newToken);
    
            if(null != doMore){
                doMore.run();
            }
        }
    
        /**
         * 踢人
         * @param m 实体
         * @param newToken token
         */
        void kickingOld(M m, String newToken);
    }
    

    M代表登录实体,也可以是能代表登录人的唯一标识,使用泛型指定。createToken用于生成并保存token,findByToken用于通过token找到登录实体,updateExpires用于更新token的过期时间,deleteToken用于删除token(登录退出的时候),getToken生成token字符串。一般验证token是几乎每个接口都会用,所以必须保证速度,可以采用redis来保存。

    /**
     * 基于redis的token管理器基类
     * @author xiongshiyan at 2018/8/15 , contact me with email yanshixiong@126.com or phone 15208384257
     */
    public abstract class AbstractRedisTokenManager<M> implements TokenManager<M> {
        protected RedisUtil redisUtil;
    
        public AbstractRedisTokenManager(RedisUtil redisUtil){
            this.redisUtil = redisUtil;
        }
    
        @SuppressWarnings("unchecked")
        @Override
        public M findByToken(String token) {
            if(null == token){
                return null;
            }
            Object o = redisUtil.get(token);
            if(null == o){
                return null;
            }
            return (M) o;
        }
    
        @Override
        public boolean deleteToken(String token){
            Object o = redisUtil.get(token);
            if(null == o){
                return false;
            }
            redisUtil.del(token);
            return true;
        }
    }
    

    此基类实现了一些公共的方法,继承此类实现剩余的方法即可。

    1.允许多设备登录的实现。这种情形下,token可以随意生成,只要保证不重复即可。

    2.只允许单设备登录,即所谓的登录踢人,在登录的时候验证是够已经登录,如果已经登录就给出提示或者直接踢人登录。这种情形下,需要根据登录的标识确认是否已经登录,有两种解决方式,一种是在保存token的时候,既保存token》》实体的关系,还需要保存实体标识与token的关系;另外一种解决方式是token与实体标识强相关,根据实体标识即可算出token。

    以下的实现既支持多设备登录又支持单设备登录,设置multi即可。

    package cn.palmte.anfang.service.token.impl;
    
    import cn.palmte.anfang.redis.RedisUtil;
    import cn.palmte.anfang.service.token.AbstractRedisTokenManager;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    /**
     * API接口token管理器主要逻辑实现【支持多设备登录、踢人】
     * @author xiongshiyan at 2018/8/15 , contact me with email yanshixiong@126.com or phone 15208384257
     */
    @SuppressWarnings("unchecked")
    public abstract class BaseMultiTokenManager<M> extends AbstractRedisTokenManager<M> {
        private static final Logger logger = LoggerFactory.getLogger(BaseMultiTokenManager.class);
    
        private long apiExpires;
        /**
         * 多设备登录 ?
         */
        private boolean multi = false;
    
        public BaseMultiTokenManager(RedisUtil redisUtil, long apiExpires , boolean multi) {
            super(redisUtil);
            this.apiExpires = apiExpires;
            this.multi = multi;
        }
    
        @Override
        public String createToken(M m) {
            String token = getToken(m);
            redisUtil.set(token , m , apiExpires);
            logger.info("createToken token = {} , m={}" , token , m.toString());
            return token;
        }
    
        @Override
        public void updateExpires(String token){
            if(null == token){
                return;
            }
            redisUtil.expire(token , apiExpires);
            if(multi){
                return;
            }
    
            //单设备需要额外同步更新
            M m = (M) redisUtil.get(token);
            if(null != m){
                redisUtil.expire(key(m) , apiExpires);
            }
        }
    
        @Override
        public boolean deleteToken(String token) {
            //单设备需要额外删除
            M m = (M) redisUtil.get(token);
    
            boolean b = super.deleteToken(token);
            if(multi){
                return b;
            }
    
            String key = key(m);
            if(null != m){
                redisUtil.del(key);
            }
            logger.info("deleteToken token = {} , key={}" , token , key);
            return b;
        }
    
        @Override
        public void kickingOld(M m, String newToken) {
            if(multi){
                throw new IllegalStateException("多设备登录情况不允许踢人");
            }
            //1.删除以前登录人的token,以前的人就通不过校验
            String withPrefix = key(m);
            String oldToken = (String) redisUtil.get(withPrefix);
            if(null == oldToken){
                return;
            }
            deleteToken(oldToken);
    
            //2.重新建立实体和新token的联系
            //单设备需要额外保存标识和token的关系
            logger.info("kickingOld key={} , value={}" , withPrefix , newToken);
            redisUtil.set(withPrefix, newToken , apiExpires);
        }
    
        /**
         * 根据实体或者标识获取key
         * @param m 实体或者标识
         * @return 返回保存标识和token关系的key
         */
        abstract protected String key(M m);
    }
    

    直接使用实体比如手机号的

    /**
     * 客户端API接口token管理器【支持多设备登录】
     * @author xiongshiyan at 2018/8/15 , contact me with email yanshixiong@126.com or phone 15208384257
     */
    public class ApiTokenManager extends BaseMultiTokenManager<String> {
        private String apiTokenPrefix;
        public ApiTokenManager(RedisUtil redisUtil, String apiTokenPrefix, long apiExpires , boolean multi) {
            super(redisUtil, apiExpires, multi);
            this.apiTokenPrefix = apiTokenPrefix;
        }
        public ApiTokenManager(RedisUtil redisUtil, String apiTokenPrefix, long apiExpires) {
            super(redisUtil , apiExpires , false);
            this.apiTokenPrefix = apiTokenPrefix;
        }
    
        @Override
        public String getToken(String m){
            return apiTokenPrefix + "-" + nowStr() + CommonUtil.randomString(16);
        }
        private String nowStr(){
            return DatetimeUtils.toStr(new Date() , DatetimeUtils.SDF_DATETIME_SHORT);
        }
    
        @Override
        protected String key(String s) {
            return apiTokenPrefix + "-" + s;
        }
    }
    

    用实体的情况

    package cn.palmte.anfang.service.token.impl.model;
    
    import cn.palmte.anfang.model.Member;
    import cn.palmte.anfang.redis.RedisUtil;
    import cn.palmte.anfang.service.token.impl.BaseMultiTokenManager;
    import top.jfunc.common.datetime.DatetimeUtils;
    import top.jfunc.common.utils.CommonUtil;
    
    import java.util.Date;
    
    /**
     * 客户端API接口token管理器【支持多设备登录】
     * @author xiongshiyan at 2018/8/15 , contact me with email yanshixiong@126.com or phone 15208384257
     */
    public class ApiTokenManager extends BaseMultiTokenManager<Member> {
        private String apiTokenPrefix;
    
        public ApiTokenManager(RedisUtil redisUtil, String apiTokenPrefix, long apiExpires , boolean multi) {
            super(redisUtil, apiExpires, multi);
            this.apiTokenPrefix = apiTokenPrefix;
        }
        public ApiTokenManager(RedisUtil redisUtil, String apiTokenPrefix, long apiExpires) {
            super(redisUtil, apiExpires, false);
            this.apiTokenPrefix = apiTokenPrefix;
        }
    
        @Override
        public String getToken(Member m){
            return apiTokenPrefix + "-" + nowStr() + CommonUtil.randomString(16);
        }
        private String nowStr(){
            return DatetimeUtils.toStr(new Date() , DatetimeUtils.SDF_DATETIME_SHORT);
        }
    
        @Override
        protected String key(Member member){
            return apiTokenPrefix + "-" + member.getPhone();
        }
    }
    

     

    展开全文
  • 默认情况下,使用邮箱推送的个人文档或从 Kindle 商店购买的电子书,都会自动存档在你的亚马逊账号下,这不仅可以节省 ...在以往,想要删除这些存档在云端的内容,无法直接在 Kindle 设备中操作,必须登录亚马逊官...

    默认情况下,使用邮箱推送的个人文档或从 Kindle 商店购买的电子书,都会自动存档在你的亚马逊账号下,这不仅可以节省 Kindle 设备的有限磁盘空间,也方便注册同一账号的的不同 Kindle 设备共享这些内容。虽然云端空间没有限制,但是对于喜欢整理的小伙伴来说,还是希望能够删除云端的一些内容。

    在以往,想要删除这些存档在云端的内容,无法直接在 Kindle 设备中操作,必须登录亚马逊官网并前往“管理我的内容和设备”操作,这在《如何单个或批量删除 Kindle 云端的电子书》这篇文章中做过详细介绍。虽然这些操作在电脑或手机浏览器均可进行,但总归没有直接在 Kindle 设备中操作方便。

    现在好消息来了,自 5.12.5 版本固件开始,Kindle 设备已原生支持永久删除云端电子书和个人文档。

    如下图所示,你可以直接长按某本电子书或个人文档(也可通过电子书或个人文档上的三点菜单按钮)调出菜单,然后点击菜单上的【永久删除】,这时会弹出一个提示框,询问你是否确定永久删除该项,如果确定就点击【是的,请删除】按钮,这样就会在删除本地文件后,同时永久删除云端的存档。

    ccf017630f060e659417f3150bda6d56.png

    0053c741add0df4339d4ee728d5aef74.png

    需要特别注意的是,这个功能对于用邮箱推送的个人文档没有什么问题,但对于从 Kindle 商店购买的电子书要格外留意,一旦出现误删,是需要重新购买的。如果你需要永久删除的只是个人文档,为了防止误删购买的电子书,书伴建议你在进行永久删除操作前,先通过“筛选器”功能筛选出“个人文档”。

    目前 Kindle 设备只支持逐一删除云端内容,若想批量删除,仍需要前往“管理我的内容和设备”操作。

    另外,如果你平时推送的个人文档数量非常多,需要经常清理,可能你并不需要存档,可以在“管理我的内容和设备”的“首选项”中找到“个人文档设置”这一项,将里面的“个人文档存档”设为禁用,这样推送的内容就只会同步到与 Kindle 邮箱相应的 Kindle 设备,而不会自动存档在你的亚马逊账户了。

    --------- · END · ---------

    【推广】书伴推送服务:push.bookfere.com

    7ff5631c72c9e40039baca6eab2eb75e.png

    2c83b3179c76b459c76c3f01c91f7562.png微信ID:kindle-fere 714161e2421956139cedac7c446cd33b.png「书伴」为静心阅读而生
    展开全文
  •  尽管SAN和NAS有很多共同特征,但二者还是有很大的差异的,其最大的差异是存储局域网是一个网络的概念,而附网存储实际上是指一种可以与网络直接相连的存储设备,它更多的是强调“设备”的概念。存储局域网考虑的是...
  • 选择U盘所在盘符(软件会自动识别第一个可移动的存储设备,用户也可以下拉选择盘符),选择使用开机更新文件还是编号文件,然后进行导入导出。串口:选择所使用的串口与波特率。请确认选择的波特率与设备波特率...
  • 用了一年多的unity的右上角的网络图标和网易云音乐的图标消失不见了,我也不记得最近有update或upgrade过,然而这两个功能还是可以正常用 解决1: 安装Gnome,果然相应的图标就出来了 现象2: Gnome3在PC上一点...

    现象1:
    用了一年多的unity的右上角的网络图标和网易云音乐的图标消失不见了,我也不记得最近有update或upgrade过,然而这两个功能还是可以正常用
    解决1:
    安装Gnome,果然相应的图标就出来了

    现象2:
    Gnome3在PC上一点不好用,很明显是针对触控设备设计的,于是在登录界面设置选择ubuntu(unity),但是登录界面却还是Gnome3的
    解决2:
    删除gnome(后来想想应该是我删除的时候使用了gnome*,所以一些和unity共享的组件也被删除了,导致unity进不去), 将.xinitrc换成unity-session,找回了unity的登录界面

    现象3:
    输入的登录密码却不能进入桌面
    解决3:
    重新安装unity,图标也回来了。

    #apt-get update 
    #apt-get install --reinstall ubuntu-desktop
    #apt-get install unity 
    #reboot 

    参考
    ubunt14.04进不了桌面(可正常输入密码进入)

    展开全文
  • 删除文件检索:可以搜索磁盘中已删除的文件,文件夹,可通过名称查看是否有违规现象。 2011/04/20: +增加了上网拨号痕迹,很多电脑使用手机当Modem会产生此类痕迹。 *修改无线网络连接痕迹部分代码,可以识别出...
  • 3、可自动识别插入的设备是USB存储设备还是非存储设备,不 会影响USB鼠标、键盘和打印机的正常使用。 4、只有管理员才能登录设置界面更改USB端口设置,也只有管 理员才能关闭或卸载软件。 5、系统一经安装...
  • 3、可自动识别插入的设备是USB存储设备还是非存储设备,不会影响USB鼠标、键盘和打印机的正常使用。 4、只有管理员才能登录设置界面更改USB端口设置,也只有管理员才能关闭或卸载软件。 5、系统一经安装,开机后...
  • 7. 删除文件检索:可以搜索磁盘中已删除的文件,文件夹,可通过名称查看是否有违规现象。 2011/04/20: +增加了上网拨号痕迹,很多电脑使用手机当Modem会产生此类痕迹。 *修改无线网络连接痕迹部分代码,可以识别出...
  • HKU Moodle Helper-crx插件

    2021-03-15 15:56:31
    您的学期列表将在设备之间同步(登录到相同Google帐户的任何Chrome浏览器):party_popper:学期结束后,点击“删除该学期的所有课程”以清空列表。 :party_popper:您可以随时选择“从本学期中删除”! suggestions有...
  • 下次再登录会询问用户希望将本地新设置上传到云端还是希望将云端的旧设置同步到本地。 云端同步: CoCoin将用户的记录同步到云端,在登录情况下,用户对记录的一切操作都会同步。 通过填写与其他用户不同的邮箱...
  • (16)系统设置:开机自动启动、麦克风设备索引、摄像头设备索引,叉掉主窗口时关闭程序还是隐藏窗口。 (17)聊天记录:支持本地保存和服务器端保存两种方式。 (18)好友分组:新增/删除分组,修改分组名称...
  • 此项设置是为了方便管理员不用准确的输入验证码就可以登录后台,同时为了保证安全性,可以在后台设置每日免验证码登录的次数,可根据你日常登录网站的规律了设定。操作方法:后台--站点设置--网站后台每日免验证码...
  • (16)系统设置:开机自动启动、麦克风设备索引、摄像头设备索引,叉掉主窗口时关闭程序还是隐藏窗口。 (17)聊天记录:支持本地保存和服务器端保存两种方式。 (18)好友分组:新增/删除分组,修改分组名称,改变...
  • 13、评论管理,可以在文章编辑页面中进入以该文章的评论进行专门管理,也可以直接管理系统的全部评论,可删除、审核、回复。 14、留言管理,功能同评论管理。 15、会员管理,管理员可在后台添加会员,此功能与前台...
  • (16)系统设置:开机自动启动、麦克风设备索引、摄像头设备索引,叉掉主窗口时关闭程序还是隐藏窗口。 (17)聊天记录:支持本地保存和服务器端保存两种方式。 (18)好友分组:新增/删除分组,修改分组名称,改变...
  • (16)系统设置:开机自动启动、麦克风设备索引、摄像头设备索引,叉掉主窗口时关闭程序还是隐藏窗口。 (17)聊天记录:支持本地保存和服务器端保存两种方式。 (18)好友分组:新增/删除分组,修改分组名称,改变...
  • (16)系统设置:开机自动启动、麦克风设备索引、摄像头设备索引,叉掉主窗口时关闭程序还是隐藏窗口。 (17)聊天记录:支持本地保存和服务器端保存两种方式。 (18)好友分组:新增/删除分组,修改分组名称,改变...
  • 但是,它已从Chrome商店中删除,因为它在uts名称中包含SoundCloud部分。 因此,它已重命名为UpNext。 可帮助您直接在Chrome浏览器中收听来自Youtube和SoundCloud的无尽音乐,而无需打开新标签。 这是一个扩展,是...
  • QQ高仿版 GG2014

    2014-11-06 16:29:49
    (16)系统设置:开机自动启动、麦克风设备索引、摄像头设备索引,叉掉主窗口时关闭程序还是隐藏窗口。 (17)聊天记录:支持本地保存和服务器端保存两种方式。 (18)好友分组:新增/删除分组,修改分组名称,改变...
  • (16)系统设置:开机自动启动、麦克风设备索引、摄像头设备索引,叉掉主窗口时关闭程序还是隐藏窗口。 (17)聊天记录:支持本地保存和服务器端保存两种方式。 (18)好友分组:新增/删除分组,修改分组名称,...
  • 14. 用户管理模块 (建立和维护可以登录此系统的账户信息) 15. 用户权限管理模块 (建立和维护本系统中账户的操作权限,例如上传,下载,阅读,编辑,删除,打印,备份,迁移,系统设置,更改密码等等) 16. 文档备份...
  • 糖果的软件

    2014-08-03 20:08:35
    登录,或者是赋予自己删除文件的权限,然后才能删除文件。要注意的是, 操作者必须拥有这些文件的所有权才能更改权限。 如果是因为文件使用了不规 范的 ACL 而不能使用某些安全工具显示或修改其权限时, 可以尝试...
  • (16)系统设置:开机自动启动、麦克风设备索引、摄像头设备索引,叉掉主窗口时关闭程序还是隐藏窗口。 (17)聊天记录:支持本地保存和服务器端保存两种方式。 (18)好友分组:新增/删除分组,修改分组名称,...
  • 此项设置是为了方便管理员不用准确的输入验证码就可以登录后台,同时为了保证安全性,可以在后台设置每日免验证码登录的次数,可根据你日常登录网站的规律了设定。操作方法:后台--站点设置--网站后台每日免验证码...
  • gg2014即时聊天

    2015-10-31 13:31:56
    (16)系统设置:开机自动启动、麦克风设备索引、摄像头设备索引,叉掉主窗口时关闭程序还是隐藏窗口。 (17)聊天记录:支持本地保存和服务器端保存两种方式。 (18)好友分组:新增/删除分组,修改分组名称,...
  • (15)最近联系人列表(16)系统设置:开机自动启动、麦克风设备索引、摄像头设备索引,叉掉主窗口时关闭程序还是隐藏窗口。(17)聊天记录:支持本地保存和服务器端保存两种方式。(18)好友分组:新增/删除分组,...
  • 高仿QQ源码

    2018-01-29 16:36:10
    16、系统设置:开机自动启动、麦克风设备索引、摄像头设备索引,叉掉主窗口时关闭程序还是隐藏窗口。 17、聊天记录:支持本地保存和服务器端保存两种方式。 18、好友分组:新增/删除分组,修改分组名称,改变好友...

空空如也

空空如也

1 2 3 4 5 6
收藏数 108
精华内容 43
关键字:

删除登录设备还是可以登录