Java微型博客系统——Redis实现防止重复登录和点赞的功能(SpringBoot+Redis)

Java微型博客系统——Redis实现防止重复登录和点赞的功能

久违地来更新一下项目。这次在之前的博客项目上加上了防止重复登录和文章的点赞功能。
Redis相关的代码写在一个新的provider中,模拟一个独立的服务器。同样将提供的服务注册在zookeeper中。该provider结构如下:
Java微型博客系统——Redis实现防止重复登录和点赞的功能(SpringBoot+Redis)_第1张图片

JedisUtils编写

JedisUtils是帮助获取redis连接的工具类。主要功能就是和redis建立连接。

package com.zhz.f.provider2.utils;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

import java.util.ResourceBundle;

public class JedisUtils {
    public static JedisPool jp = null;
    public static String host = null;
    public static Integer port = null;
    public static Integer MaxTotal = null;
    public static Integer MaxIdle = null;

    static {
        //从properties中取出数据
        ResourceBundle rb = ResourceBundle.getBundle("Jedis");
        host = rb.getString("redis.host");
        port = Integer.parseInt(rb.getString("redis.port"));
        MaxTotal = Integer.parseInt(rb.getString("redis.MaxTotal"));
        MaxIdle = Integer.parseInt(rb.getString("redis.MaxIdle"));
        //新建配置类
        JedisPoolConfig jpc = new JedisPoolConfig();
        jpc.setMaxTotal(MaxTotal);//最大链接数
        jpc.setMaxIdle(MaxIdle);//最大活动数
        //新建Jedis池
        jp = new JedisPool(jpc,host,port);
    }
    public static Jedis getJedis(){
        return jp.getResource();
    }
}

其中properties中存的是一些连接参数

redis.host=127.0.0.1
redis.port=6379
redis.MaxTotal=30
redis.MaxIdle=10

防止登录重复

Redis的业务

1.创建一个set类型的key“account”来保存登录过的账号。
2.当有新账号登录时,使用sadd添加,根据返回结果可以判定账号是否存在(0:账号已登录,添加失败;1:账号未登录,添加成功)
3.账号退出时,账号对应的将member删除

Redis接口

注意:接口是在共用的api包下的

package com.zhz.f.api.service;
public interface MyRedis {
    boolean registerAccountInRedis(String account);
    boolean logoutAccountInRedis(String account);
}

Redis接口实现

@DubboService
@Service
public class MyRedisService implements MyRedis {
    private static final Jedis jedis = JedisUtils.getJedis();

    @Override
    public boolean registerAccountInRedis(String account) {
        //如果账号存在则返回false,不存在则成功添加,返回true
        Long ifAdded = jedis.sadd("account", account);
        return ifAdded == 1L;
    }

    @Override
    public boolean logoutAccountInRedis(String account) {
        Long aLong = jedis.srem("account", account);
        return aLong == 1L;
    }

登录时处理

在client服务器的LoginController中添加判断代码,防止重复登录。
在确定输入账号密码正确后,加入判断,如果账号存在则返回错误信息,不存在则可以正常登录。(为了更好看见新代码,删掉了其它代码的注释)

package com.zhz.f.client.controller.account;

@Controller
public class LoginController {
    @DubboReference
    private MyData myData;
    @DubboReference
    private MyRedis myRedis;
    @RequestMapping("/login")
    public String loginServer(HttpServletRequest request, Model model, HttpSession session){
        if(session.getAttribute("userAccount")!=null){
            return "redirect:/zhz/toUserMain";
        }
        String userAccount = request.getParameter("userAccount");
        String userPassword = request.getParameter("userPassword");
        if (userAccount==null)return "/account/login";
        if(myData.comparePasswordsByAccount(userAccount,userPassword)){
            session.setAttribute("userAccount",userAccount);
            //将账号注册到redis,成功返回true,失败返回false说明已经存在
            if(!myRedis.registerAccountInRedis(userAccount)){
                model.addAttribute("callback","该账号已经登录");
                return "/account/login";
            }
            //进入主界面
            return "redirect:/zhz/toUserMain";
        }else {
            model.addAttribute("callback","输入账号或密码错误");
            return "/account/login";
        }
    }
}

效果图:
Java微型博客系统——Redis实现防止重复登录和点赞的功能(SpringBoot+Redis)_第2张图片

退出时处理

在client服务器的LogoutController中添加判断代码,去除已退出的账号。
移除登录信息之前先删除Redis中保存的登录记录。

package com.zhz.f.client.controller.account;

import com.zhz.f.api.service.MyRedis;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.http.HttpSession;

@Controller
public class LogoutController {
    @DubboReference
    private MyRedis myRedis;
    @RequestMapping("/zhz/logout")
    public String logout(HttpSession session){
        //删掉redis中保存的登录账号
        myRedis.logoutAccountInRedis((String) session.getAttribute("userAccount"));
        //删掉login中放入session中的userAccount
        session.removeAttribute("userAccount");
        return "/account/login";
    }
}

未点击注销直接退出浏览器处理

目前这个防止重复的功能其实是有点问题的,当用户在不退出账号而关闭页面时,虽然下次直接访问界面还能访问(因为我在session中存了账号信息,下次会直接登录,具体实现见前面的博客),但退出浏览器后就不能再次登录(redis没有将账号信息删掉)。这样再次登录就会提示已登录,该账号相当于永远都不能再登录了(除非我在redis里手动删掉它)。
这时可以添加一个Session的监听器,来监听Session的销毁(浏览器的关闭)
实现的话只需要新建一个类实现HttpSessionListener 接口,并重写sessionDestroyed方法完成所需要的功能即可。
(勘误:在浏览器退出后,服务器中的session不会立即销毁,而是一段时间后销毁)

public class AllSessionListener implements HttpSessionListener {
    @DubboReference
    private MyRedis myRedis;
    @Override
    //检测到浏览器退出,将session中登录的信息给删除
    public void sessionDestroyed(HttpSessionEvent se) {
        HttpSession session = se.getSession();
        String userAccount = (String) session.getAttribute("userAccount");
        //如果未登录或者已经退出,session中将没有userAccount信息,所以不用去Redis中删除(防止误删或出现错误)。
        if(userAccount!=null&&(!"".equals(userAccount)))
            myRedis.logoutAccountInRedis((String) session.getAttribute("userAccount"));
    }
}

小结

这里为了防止重复登录其实有很多其它的方法,这里我只是为了练手而使用了Redis,Spring security等也可以防止用户重复登录(但我还没学)。

点赞功能

Redis的业务

(这边一些变量的名字取的有些随意,写的时候没有想清除就写了。。。)
1.创建hash类型的key:“good”,field:“文章名字:文章所有者:点赞者”,value:“点赞者”。(这个value暂时没有用上,其实也可以直接用string类型来存,但我觉得用hash的好处是所有的点赞信息都归纳在一个key中,会便于管理一点)
2.点赞的时候使用hsetnx方法,该用户已经给该文章点过赞,则删除该field,并将文章的点赞数-1;如果没有点赞则加上该field,并将文章的点赞数+1.

Redis接口

package com.zhz.f.api.service;

public interface MyRedis {
    boolean goodGood(String articleOwner,String articleName,String user);
    int countOfGood(String articleOwner,String articleName);
}

接口的实现

package com.zhz.f.provider2.service;

import com.zhz.f.api.service.MyRedis;
import com.zhz.f.provider2.utils.JedisUtils;
import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.stereotype.Service;
import redis.clients.jedis.Jedis;

@DubboService
@Service
public class MyRedisService implements MyRedis {
    private static final Jedis jedis = JedisUtils.getJedis();
    @Override
    public boolean goodGood(String articleOwner, String articleName, String user) {
        //尝试添加,如果没有赞过,添加赞
        Long ifAdded = jedis.hsetnx("good", articleOwner + ":" + articleName + "goodBy" + user, user);
        //添加成功后,为赞的次数+1
        if (ifAdded == 1L) {
            //如果是第一次被赞,计数为1
            Long ifFirst = jedis.hsetnx("good", articleOwner + ":" + articleName + "goodCount", "1");
            //如果不是第一次,添加失败,ifFirst返回0,计数+1
            if (ifFirst == 0L) {
                jedis.hincrBy("good", articleOwner + ":" + articleName + "goodCount", 1L);
            }
            return true;
        }
        //添加失败,说明已经赞过了,将赞取消,将对应的count-1
        jedis.hdel("good", articleOwner + ":" + articleName + "goodBy" + user);
        jedis.hincrBy("good", articleOwner + ":" + articleName + "goodCount", -1L);

        return false;
    }

    @Override
    public int countOfGood(String articleOwner, String articleName) {
        String hget = jedis.hget("good", articleOwner + ":" + articleName + "goodCount");
        //当从来没有没有点过赞的时候,该field不存在,会返回null。
        if (hget==null){
            return 0;
        }
        return Integer.parseInt(hget);
    }
}

点赞处理

在ToArticleController中获取被点赞数,传给前端:

//获取被赞的数量
  int countOfLikes = myRedis.countOfGood(articleOwnerAccount, articleTitle);
  model.addAttribute("countOfLikes", countOfLikes);

前端代码:

为了展示新添加的点赞功能,将内容显示和评论功能略去了,感兴趣的可以看看我之前的文章。


<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
。。。
head>
<body>

<h2>
    <div th:text="${articleTitle}">div>
h2>
。。。

<h3>如果你喜欢请给该作品点赞h3>
<form action="/zhz/likeArticle">
    <input type="image" src="../static/images/good.jpg" th:src="@{/images/good.jpg}" height="50px" width="50px"
           name=good title="点赞或取消" onclick="this.form.submit()">
    <input name="articleOwnerAccount" th:value="${articleOwnerAccount}" hidden>
    <input name="articleTitle" th:value="${articleTitle}" hidden>
form>
<h3>已点赞数:<strong th:text="${countOfLikes}">strong>h3>

。。。
form>

body>
html>

效果如下:
Java微型博客系统——Redis实现防止重复登录和点赞的功能(SpringBoot+Redis)_第3张图片
(我的前端做的还是比较简陋的,请大家见谅)

后端代码

@Controller
public class LikeArticlesController {
    @DubboReference
    private MyRedis myRedis;
    @RequestMapping("/zhz/likeArticle")
    public String likeArticle(HttpServletRequest request, HttpSession session, Model model){
        String articleOwner = request.getParameter("articleOwnerAccount");
        String articleTitle = request.getParameter("articleTitle");
        String userAccount = (String) session.getAttribute("userAccount");
        myRedis.goodGood(articleOwner, articleTitle, userAccount);

        return "redirect:/zhz/toUserMain";
    }
}

小结

这样点赞功能就实现了。

你可能感兴趣的:(java,redis,web)