仿牛客网的bbs(三)

237 阅读2分钟

学习记录
使用kaptcha 发送验证码
aptcha 的工作原理是调用
com.google.code.kaptcha.servlet.KaptchaServlet ,生成一个验证码图片,响应到客户端,同时将生成的真是的验证码字符串放到 HttpSession 中。
pom添加依赖

<dependency>
   <groupId>com.github.penggle</groupId>
   <artifactId>kaptcha</artifactId>
   <version>2.3.2</version>
</dependency>

添加一个配置类,配置kaptcha

@Configuration
public class KaptchaConfig {
    @Bean
    public Producer kaptchaProducer() {
        Properties properties = new Properties();
        properties.setProperty("kaptcha.image.width", "100");
        properties.setProperty("kaptcha.image.height", "40");
        properties.setProperty("kaptcha.textproducer.font.size", "32");
        properties.setProperty("kaptcha.textproducer.font.color", "0,0,0");
        properties.setProperty("kaptcha.textproducer.char.string", "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYAZ");
        // 干扰,防破解
        properties.setProperty("kaptcha.textproducer.char.length", "4");
        properties.setProperty("kaptcha.noise.impl", "com.google.code.kaptcha.impl.NoNoise");


        DefaultKaptcha kaptcha = new DefaultKaptcha();
        Config config = new Config(properties);
        kaptcha.setConfig(config);
        return kaptcha;
    }
}

controller里生成验证码

@RequestMapping(path = "/kaptcha", method = RequestMethod.GET)
public void getKaptcha(HttpServletResponse response/*, HttpSession session*/) {
    // 生成验证码
    String text = kaptchaProducer.createText();
    BufferedImage image = kaptchaProducer.createImage(text);

    // 将验证码存入session
    //  敏感数据最好用session ,因为服务器端比较安全
    // session.setAttribute("kaptcha", text);

    // 验证码的归属
    String kaptchaOwner = CommunityUtil.generateUUID();
    Cookie cookie = new Cookie("kaptchaOwner", kaptchaOwner);
    cookie.setMaxAge(60);
    cookie.setPath(contextPath);
    response.addCookie(cookie);
    // 将验证码存入Redis
    String redisKey = RedisKeyUtil.getKaptchaKey(kaptchaOwner);
    redisTemplate.opsForValue().set(redisKey, text, 60, TimeUnit.SECONDS);

    // 将图片输出给浏览器
    response.setContentType("image/png");
    try {
        OutputStream os = response.getOutputStream();
        ImageIO.write(image, "png", os);
    } catch (IOException e) {
        logger.error("响应验证码失败:" + e.getMessage());
    }
}

前端实现点击刷新验证码

<div class="col-sm-4">
   <img th:src="@{/kaptcha}" id="kaptcha" style="width:100px;height:40px;" class="mr-2"/>
   <a href="javascript:refresh_kaptcha();" class="font-size-12 align-bottom">刷新验证码</a>
</div>

实现refresh_kaptcha方法

<script th:src="@{/js/global.js}"></script>
<script>
   function refresh_kaptcha() {
      var path = CONTEXT_PATH + "/kaptcha?p=" + Math.random();
      $("#kaptcha").attr("src", path); // 改属性
   }
</script>
// 注意CONTEXT_PATH 在 global.js里添加 var CONTEXT_PATH = "/community";

发布帖子
该功能使用Ajax
添加一个方法把map转json

public static String getJSONString(int code, String msg, Map<String, Object> map) {
    JSONObject json = new JSONObject();
    json.put("code", code);
    json.put("msg", msg);
    if (map != null) {
        for (String key : map.keySet()) {
            json.put(key, map.get(key));
        }
    }
    return json.toJSONString();
}

public static String getJSONString(int code, String msg) {
    return getJSONString(code, msg, null);
}

public static String getJSONString(int code) {
    return getJSONString(code, null, null);
}

ajax

// ajax示例
@RequestMapping(path = "/ajax", method = RequestMethod.POST)
@ResponseBody
public String testAjax(String name, int age) {
    System.out.println(name);
    System.out.println(age);
    return CommunityUtil.getJSONString(0, "操作成功!");
}

在前端写个测试类

 <p>
        <input type="button" value="发送" onclick="send();">
  </p>
    <script src="https://code.jquery.com/jquery-3.3.1.min.js" crossorigin="anonymous"></script>
    <script>
        function send() {
            // $.ajax()是万能方法
            $.post(
                "/community/alpha/ajax", // 访问路径
                {"name":"张三","age":23},  // 向服务器提交的数据
                function(data) { // 回调函数
                    console.log(typeof(data));
                    console.log(data);

                    data = $.parseJSON(data);
                    console.log(typeof(data));
                    console.log(data.code);
                    console.log(data.msg);
                }
            );
        }
    </script>

在Mapper接口里添加

int insertDiscussPost(DiscussPost discussPost);

mapper.xml里添加sql

<sql id="insertFields">
    user_id, title, content, type, status, create_time, comment_count, score
</sql>
<insert id="insertDiscussPost" parameterType="DiscussPost" keyProperty="id">
    insert into discuss_post(<include refid="insertFields"></include>)
    values(#{userId},#{title},#{content},#{type},#{status},#{createTime},#{commentCount},#{score})
</insert>

以前是dao层写接口和接口实现类,在类里写jdbc的sql语句
service里面对添加操作做敏感词过滤,实现插入帖子

public int addDiscussPost(DiscussPost post) {
    if (post == null) {
        throw new IllegalArgumentException("参数不能为空!");
    }

    // 转义HTML标记
    post.setTitle(HtmlUtils.htmlEscape(post.getTitle()));
    post.setContent(HtmlUtils.htmlEscape(post.getContent()));
    // 过滤敏感词
    post.setTitle(sensitiveFilter.filter(post.getTitle()));
    post.setContent(sensitiveFilter.filter(post.getContent()));

    return discussPostMapper.insertDiscussPost(post); // 调用插入操作
}

controller里实现添加帖子

@RequestMapping(path = "/add", method = RequestMethod.POST)
@ResponseBody
public String addDiscussPost(String title, String content) {
    User user = hostHolder.getUser();
    if (user == null) {
        return CommunityUtil.getJSONString(403, "你还没有登录哦!");
    }

    DiscussPost post = new DiscussPost();
    post.setUserId(user.getId());
    post.setTitle(title);
    post.setContent(content);
    post.setCreateTime(new Date());
    discussPostService.addDiscussPost(post);

    // 触发发帖事件
    Event event = new Event()
            .setTopic(TOPIC_PUBLISH)
            .setUserId(user.getId())
            .setEntityType(ENTITY_TYPE_POST)
            .setEntityId(post.getId());
    eventProducer.fireEvent(event);

    // 计算帖子分数
    String redisKey = RedisKeyUtil.getPostScoreKey();
    redisTemplate.opsForSet().add(redisKey, post.getId());

    // 报错的情况,将来统一处理.
    return CommunityUtil.getJSONString(0, "发布成功!");
}

视图层

<div class="modal-body">
   <form>
      <div class="form-group">
         <label for="recipient-name" class="col-form-label">标题:</label>
         <input type="text" class="form-control" id="recipient-name">
      </div>
      <div class="form-group">
         <label for="message-text" class="col-form-label">正文:</label>
         <textarea class="form-control" id="message-text" rows="15"></textarea>
      </div>
   </form>
</div>

过程
(视图的数据从controller来,controller调用service的方法,service调用mapper接口的方法,接口方法找mapper.xml)

数据获取在index.js中

// 获取标题和内容
var title = $("#recipient-name").val();
var content = $("#message-text").val();