简单的系统登录案例

本案例是使用的简单token作为验证;而token与用户信息会保存在redis中;

简单说明一下本案例的一些逻辑,可能存在某些漏洞,希望大家指正;


用户未登录时,访问系统中任意controller方法都将跳转至登录页面;

用户使用URL访问系统中任意页面(我们是将页面放在了WEB-INF之外的)都将跳转至登录页面;

用户已登录,每次请求服务器,将token超时时间延后30分钟;

用户已登录,访问登录页面都将跳转至已登录的首页;

session超时将清除redis中token,平台的token超时将清除session中所有信息;

用户登出,清除session信息,清除Redis中的token信息;


redis相关代码:

jedis连接池:

public class JedisClientPool {
    
    private static JedisClientPool jedisClientPool = new JedisClientPool();
    private static JedisPool pool;

    //redis连接相关属性
    private static String host;
    private static Integer port;
    private static String password;
    private static Integer timeout;
    private static Integer poolMaxtotal;
    private static Integer poolMaxIdle;
    private static Long poolMaxwaitMillis;

    private JedisClientPool(){
    }

    static{
        //自定义的properties读取器,获取properties文件中的值;
        host = PropertiesHandler.get(String.class,"redis_host");
        port = PropertiesHandler.get(Integer .class,"redis_port");
        password = PropertiesHandler.get(String.class,"redis_password");
        timeout = PropertiesHandler.get(Integer.class,"redis_timeout");
        poolMaxtotal = PropertiesHandler.get(Integer.class,"redis_pool_maxtotal");
        poolMaxIdle = PropertiesHandler.get(Integer.class,"redis_pool_maxIdle");
        poolMaxwaitMillis = PropertiesHandler.get(Long.class,"redis_pool_maxwaitMillis");
    }

    //初始化jedisPool对象;
    private static void initJedisPool(){
        JedisPoolConfig jedisPoolConfig = initPoolConfig();
        pool = new JedisPool(jedisPoolConfig,host,port,timeout);
    }
    //初始化jedisPoolConfig对象;
    private static JedisPoolConfig initPoolConfig(){
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        jedisPoolConfig.setMaxIdle(poolMaxIdle);
        jedisPoolConfig.setMaxTotal(poolMaxtotal);
        jedisPoolConfig.setMaxWaitMillis(poolMaxwaitMillis);
        return jedisPoolConfig;
    }
    //单例模式,确保系统中只有一个Jedis连接池;
    public static JedisClientPool getInstance(){
        if(pool == null){
            synchronized (JedisClientPool.class){
                if(pool == null){
                    initJedisPool();
                }
            }
        }
        return jedisClientPool;
    }
    //获取jedis连接,可以在返回连接之前做密码验证;
    public Jedis getResource() {
        Jedis resource = pool.getResource();
//        resource.auth(password);
        return resource;
    }
    //归还连接给连接池;
    public void returnResource(Jedis jedis){
        jedis.close();
    }
}

操作redis的工具类:

public class RedisCache {
    //得到一个自定义的jedis连接池对象
    private static JedisClientPool pool = JedisClientPool.getInstance();

    private final static int TIME_OUT_SECOND = 60;
    /**
     * 从redis连接池中获取一个连接
     * @return
     */
    private static Jedis getJedis(){
        Jedis resource = pool.getResource();
        return resource;
    }


    /**
     * 归还一个连接给redis连接池
     * @param jedis
     */
    private static void returnJedis(Jedis jedis){
        pool.returnResource(jedis);
    }

    /**
     * 判断redis中该key是否存在
     * @param key
     * @return
     */
    public static Boolean exists(final String key) {
        Jedis jedis = getJedis();
        Boolean exists = jedis.exists(StrUtil.encodeStr(key));
        returnJedis(jedis);
        return exists;
    }

    /**
     * 获取redis中指定key的值
     * @param key
     * @return
     */
    public static String get(String key){
        Jedis jedis = getJedis();
        String string = StrUtil.decodeToString(jedis.get(StrUtil.encodeStr(key)));
        returnJedis(jedis);
        return string;
    }

    /**
     * 向redis中设置一对key-value
     * @param key
     * @param value
     */
    public static void set(String key ,String value){
        Jedis jedis = getJedis();
        jedis.set(StrUtil.encodeStr(key), StrUtil.encodeStr(value));
        returnJedis(jedis);

    }

    /**
     * 删除redis中指定的key
     * @param key
     */
    public static void del(String key){
        Jedis jedis = getJedis();
        jedis.del(StrUtil.encodeStr(key));
        returnJedis(jedis);
    }

    /**
     * 为redis中指定key设置超时时间
     * @param key
     * @param seconds
     */
    public static void expire(String key,int seconds){
        Jedis jedis = getJedis();
        jedis.expire(StrUtil.encodeStr(key),seconds);
        returnJedis(jedis);
    }
}

redis相关已经完毕,接下来就是过滤器的一些规则:

token过滤器,由于我们使用的是nutz框架,所以继承的是框架内的filter,返回也不一样,但是对于session和token的验证还是可以复用到其他过滤器中;

public class TokenFilter implements ActionFilter {
    @Override
    public View match(ActionContext actionContext) {
        HttpServletRequest request = actionContext.getRequest();
        String pathInfo = request.getRequestURI();
        System.out.println("requestPathis : "+pathInfo);


        HttpSession session = request.getSession();
        if(session == null){//session为空,跳转登录页面;
            return new DlxView("/login.html");
        }
        String authorization = (String) session.getAttribute("authorization");
        if(StrUtil.isBlank(authorization)){
            if(StrUtil.hasVal(pathInfo) && pathInfo.contains("/NDAS/login/")){
                //登录模块中的请求方法都可直接通过;
                //session中没有token,且路径是去往登录方法时,允许通过
                return null;
            }else{
                //否则去往登录页面且清除session
                session.invalidate();
                return new DlxView("/login.html");
            }
        }
        //session中存在token,去redis中验证
        Boolean exists = RedisCache.exists(authorization);
        if(!exists){//如果redis中不存在token,跳转至登录页面
            System.out.println("redis token is not exists!!!");
            session.invalidate();//并清理session;
            return new DlxView("/login.html");
        }else{//如果redis中存在token,那么证明此次用户操作有效,则将token有效时长设置为指定时间之后
            System.out.println(authorization + " will timeout after " + 1800 + " seconds!!!");
            RedisCache.expire(authorization,1800);
        }
        return null;
    }
}

由于我们的页面时放在WEB-INF之外,所以需要一个静态资源过滤器:

public class StaticFilter implements Filter {

    Log log = Logs.get();

    private static List<String> SUFFIX_LIST =new ArrayList<>();
    static {
        //初始化非页面静态资源后缀;
        SUFFIX_LIST.add("png");
        SUFFIX_LIST.add("gif");
        SUFFIX_LIST.add("jpg");
        SUFFIX_LIST.add("js");
        SUFFIX_LIST.add("css");
        SUFFIX_LIST.add("jspx");
        SUFFIX_LIST.add("jpeg");
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        log.info("contextPath is "+filterConfig.getServletContext().getContextPath());
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        //将filter中的servletRequest,servletResponse强转为http格式的;方便后面获取url和session;
        HttpServletResponse resp = (HttpServletResponse) response;
        HttpServletRequest req = (HttpServletRequest) request;
        //从request中获取url;
        String requestURL = req.getRequestURL().toString();
        //从request中获取session;
        HttpSession session = req.getSession();
        //获取session中的token;
        String token = (String) session.getAttribute("authorization");


        int lastIndexOf = requestURL.lastIndexOf(".");
        int lastIndexOf1 = requestURL.lastIndexOf("?");
        //首先暂定为没有带参数的url
        String suffix = requestURL.substring(lastIndexOf + 1);
        //根据判断,如果是带参数的url则重新切割
        if(lastIndexOf1 != -1 && lastIndexOf1 > lastIndexOf){
            suffix = requestURL.substring(lastIndexOf+1,lastIndexOf1);
        }

        if(lastIndexOf == -1){
            log.info("url has no suffix");
            //没有后缀,将会被重定向到登录首页;
            resp.sendRedirect("http://localhost:8090/XXXX/XXXX/toLogin.xhtml");
            return;
        }else{
            if(!SUFFIX_LIST.contains(suffix)&&("jsp".equals(suffix)||"html".equals(suffix))&&!"xhtml".equals(suffix)){
                //后缀匹配不上,重定向到首页;
                resp.sendRedirect("http://localhost:8090/XXXX/XXXX/toLogin.xhtml");
                return;
            }
        }
        //如果没有token,则对html和jsp的直接访问url进行拦截
        if(StrUtil.isBlank(token)){
            //如果url以/结尾,则表示是直接访问系统中的index.jsp或者index.html
            if(requestURL.endsWith("/")){
                log.info("url endWith '/'");
                //如果是直接访问index.jsp,则重定向到登录首页
                resp.sendRedirect("http://localhost:8090/XXXX/XXXX/toLogin.xhtml");
                return;
            }else{
                if(StrUtil.hasVal(suffix)){
                    if(SUFFIX_LIST.contains(suffix)){
                        //普通静态资源不做处理;
                        chain.doFilter(request,response);
                        return ;
                    }
                    if("jsp".equals(suffix)||"html".equals(suffix)){
                        log.info("url endWith jsp or html");
                        //直接访问jsp或者html页面,将会被重定向到登录首页;
                        resp.sendRedirect("http://localhost:8090/XXXX/XXXX/toLogin.xhtml");
                        return;
                    }
                }
            }
        }

        chain.doFilter(request,response);
    }

    @Override
    public void destroy() {
        log.info("staticFilter is destroy");
    }
}

token(UUID)生成器:

public class UUIDFactory {

    /**
     * 获取一个32位的UUID;
     * @return
     */
    public static String getUUID(){
        String uid = UUID.randomUUID().toString().replace("-", "");
        return uid;
    }

    /**
     * 获取一个指定长度的UUID
     * @param length
     * @return
     */
    public static String getUUID(int length){
        String uid = UUID.randomUUID().toString().replace("-", "");
        uid = uid.substring(0, length);
        return uid;
    }
}

web.xml配置(注意,如果tokenfilter是实现的filter则也要在web.xml中配置)

    <filter>
        <filter-name>staticFilter</filter-name>
        <filter-class>com.XXXX.XXXX.XXXX.StaticFilter</filter-class>
    </filter>

    <filter-mapping>
        <filter-name>staticFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>


    <listener>
        <listener-class>com.XXXX.XXXX.XXXX.DlxSessionListener</listener-class>
    </listener>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值