【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、

这篇具有很好参考价值的文章主要介绍了【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

😀如果对你有帮助的话😊
🌺为博主点个赞吧 👍
👍点赞是对博主最大的鼓励😋
💓爱心发射~💓

【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、

一、发送邮件

【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、

1、启用客户端SMTP服务

【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、

【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、
bofryuzursekbiab——密码

2、导入jar包

【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、

<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-mail -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-mail</artifactId>
    <version>2.7.0</version>
</dependency>

3、邮箱参数配置

  • 访问邮箱域名
  • 邮箱端口
  • 账号
  • 密码
  • 协议
  • 详细配置
# MailProperties
spring.mail.host=smtp.sina.com
spring.mail.port=465
spring.mail.username=@.com
spring.mail.password=nowcoder123
spring.mail.protocol=smtps
spring.mail.properties.mail.smtp.ssl.enable=true

MailClient

【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、

package com.nowcoder.community.util;

@Component
public class MailClient {

    private static final Logger logger = LoggerFactory.getLogger(MailClient.class);

    @Autowired
    private JavaMailSender mailSender;

    @Value("${spring.mail.username}")
    private String from;

    public void sendMail(String to, String subject, String content) {
        try {
            MimeMessage message = mailSender.createMimeMessage();
            MimeMessageHelper helper = new MimeMessageHelper(message);
            helper.setFrom(from);
            helper.setTo(to);
            helper.setSubject(subject);
            helper.setText(content, true);
            mailSender.send(helper.getMimeMessage());
        } catch (MessagingException e) {
            logger.error("发送邮件失败:" + e.getMessage());
        }
    }

}

demo.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>邮件示例</title>
</head>
<body>
    <p>欢迎你, <span style="color:red;" th:text="${username}"></span>!</p>
</body>
</html>

MailTests


@RunWith(SpringRunner.class)
@SpringBootTest
@ContextConfiguration(classes = CommunityApplication.class)
public class MailTests {

    @Autowired
    private MailClient mailClient;

    @Autowired
    private TemplateEngine templateEngine;

    @Test
    public void testTextMail() {
        mailClient.sendMail("1724206051@qq.com", "TEST", "Welcome.");
    }

    @Test
    public void testHtmlMail() {
        Context context = new Context();
        context.setVariable("username", "sunday");

        String content = templateEngine.process("/mail/demo", context);
        System.out.println(content);

        mailClient.sendMail("1724206051@qq.com", "HTML", content);
    }

}

总结

  1. JavaMailSenderSpring Email的核心组件,负责发送邮件
  2. MimeMessage用于封装邮件的相关信息
  3. MimeMessageHelper用于辅助构建MimeMessage对象
  4. TemplateEngine是模板引擎,负责格式化HTML格式的邮件

Spring Boot对发送邮件提供了支持,可以通过MailProperties对邮件进行配置

  • 可以配置邮件服务器的域名和端口
  • 可以配置发件人的账号及密码
  • 可以配置发送邮件的协议类型

哪些会被Spring Boot自动装配到Spring容器中

  • JavaMailSender
  • TemplateEngine

二、开发注册功能

【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、

1、访问注册页面

点击顶部区域内的链接,打开注册页面。

修改——thymeleaf

【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、

【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、

首页—超链接——index.html

【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、

每个html头部复用——index.html

【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、

LoginController

【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、
返回注册页面

@Controller
public class LoginController {
    @Autowired
    private UserService userService;

    @RequestMapping(path = "/register", method = RequestMethod.GET)
    public String getRegisterPage() {
        return "/site/register";
    }
}

2、提交注册数据

通过表单提交数据。

添加依赖和配置

<dependency>
	<groupId>org.apache.commons</groupId>
	<artifactId>commons-lang3</artifactId>
	<version>3.9</version>
</dependency>
# community
community.path.domain=http://localhost:8080

service层

  • 服务端发送激活邮件。
工具类——CommunityUtil

生成随机字符串

给文件生成随机名字
【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、

public class CommunityUtil {

    // 生成随机字符串
    public static String generateUUID() {
        return UUID.randomUUID().toString().replaceAll("-", "");
    }

    // MD5加密 : 只能加密,不能解密
    // hello -> abc123def456
    // hello + 3e4a8 -> abc123def456abc
    // 先加字符串 , 再加密
    public static String md5(String key) {
        // 参数为空,不加密
        if (StringUtils.isBlank(key)) {
            return null;
        }
        return DigestUtils.md5DigestAsHex(key.getBytes());
    }


}

注入——UserService

【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、

  • 注入邮件客户端
  • 注入模板引擎
@Service
public class UserService {

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private MailClient mailClient;  // 邮件客户端

    @Autowired
    private TemplateEngine templateEngine;  // 模板引擎

    @Value("${community.path.domain}")
    private String domain;    // 域名

    @Value("${server.servlet.context-path}")
    private String contextPath;   // 项目名

    public User findUserById(int id) {
        return userMapper.selectById(id);
    }

    public Map<String, Object> register(User user){
        Map<String, Object> map = new HashMap<>();  // map实例化
        // 空值处理
        if (user == null) {
            throw new IllegalArgumentException("参数不能为空!");
        }
        if (StringUtils.isBlank(user.getUsername())) {
            map.put("usernameMsg", "账号不能为空!");
            return map;
        }
        if (StringUtils.isBlank(user.getPassword())) {
            map.put("passwordMsg", "密码不能为空!");
            return map;
        }
        if (StringUtils.isBlank(user.getEmail())) {
            map.put("emailMsg", "邮箱不能为空!");
            return map;
        }

        // 验证账号
        User u = userMapper.selectByName(user.getUsername());
        if (u != null) {
            map.put("usernameMsg", "该账号已存在!");
            return map;
        }

        // 验证邮箱
        u = userMapper.selectByEmail(user.getEmail());
        if (u != null) {
            map.put("emailMsg", "该邮箱已被注册!");
            return map;
        }

        // 注册用户
        user.setSalt(CommunityUtil.generateUUID().substring(0, 5));    //生成随机字符串
        user.setPassword(CommunityUtil.md5(user.getPassword() + user.getSalt()));   //密码拼接
        user.setType(0);    // 类型
        user.setStatus(0);  //状态
        user.setActivationCode(CommunityUtil.generateUUID());   //激活码
        user.setHeaderUrl(String.format("http://images.nowcoder.com/head/%dt.png", new Random().nextInt(1000)));   //头像
        user.setCreateTime(new Date());     //  创建时间
        userMapper.insertUser(user);    //添加库里

        // 激活邮件
        Context context = new Context();
        context.setVariable("email", user.getEmail());
        // http://localhost:8081/community/activation/101/code
        // 域名——项目名——功能访问名 + 用户 id   激活码
        String url = domain + contextPath + "/activation/" + user.getId() + "/" + user.getActivationCode();
        context.setVariable("url", url);
        String content = templateEngine.process("/mail/activation", context);
        mailClient.sendMail(user.getEmail(), "激活账号", content);      // 标题 内容

        return map;
    }
}

牛客网随机头像
【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、

改造模板——activation.html

【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、

<!doctype html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
	<meta charset="utf-8">
	<link rel="icon" href="https://static.nowcoder.com/images/logo_87_87.png"/>
	<title>牛客网-激活账号</title>
</head>
<body>
<div>
	<p>
		<b th:text="${email}">xxx@xxx.com</b>, 您好!
	</p>
	<p>
		您正在注册牛客网, 这是一封激活邮件, 请点击
		<a th:href="${url}">此链接</a>,
		激活您的牛客账号!
	</p>
</div>
</body>
</html>

控制层

  • 服务端验证账号是否已存在、邮箱是否已注册。

【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、

  • 注册成功——到首页进行激活——在登陆

@{}:路径是动态的
${}:里边是变量

注册成功或有错误返回——LoginController
    @RequestMapping(path = "/register", method = RequestMethod.POST)
    public String register(Model model, User user) {
        Map<String, Object> map = userService.register(user);
        if (map == null || map.isEmpty()) {
            model.addAttribute("msg", "注册成功,我们已经向您的邮箱发送了一封激活邮件,请尽快激活!");
            model.addAttribute("target", "/index");
            return "/site/operate-result";
        } else {
            model.addAttribute("usernameMsg", map.get("usernameMsg"));
            model.addAttribute("passwordMsg", map.get("passwordMsg"));
            model.addAttribute("emailMsg", map.get("emailMsg"));
            return "/site/register";
        }
    }
激活成功模板——operate-result.html
账号、密码、邮箱错误——返回register.html

默认值的显示
【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、
【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、


【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、

3、激活注册账号

点击邮件中的链接,访问服务端的激活服务。

在service层加一个业务,几种情况:

  • 激活成功
  • 多次点击激活链接
  • 重复激活给提示
  • 激活码伪造

三种结果:

  • 成功
  • 重复激活
  • 失败

常量接口——CommunityConstant

【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、

public interface CommunityConstant {

    /**
     * 激活成功
     */
    int ACTIVATION_SUCCESS = 0;

    /**
     * 重复激活
     */
    int ACTIVATION_REPEAT = 1;

    /**
     * 激活失败
     */
    int ACTIVATION_FAILURE = 2;

}

UserService

    public int activation(int userId, String code) {
        User user = userMapper.selectById(userId);
        // 看状态、 激活码
        if (user.getStatus() == 1) {
            return ACTIVATION_REPEAT;
        } else if (user.getActivationCode().equals(code)) {
            userMapper.updateStatus(userId, 1);
            return ACTIVATION_SUCCESS;
        } else {
            return ACTIVATION_FAILURE;
        }
    }

返回页面——LoginController


    @RequestMapping(path = "/login", method = RequestMethod.GET)
    public String getLoginPage() {
        return "/site/login";
    }
    //处理请求
    // http://localhost:8080/community/activation/101/code
    @RequestMapping(path = "/activation/{userId}/{code}", method = RequestMethod.GET)
    public String activation(Model model, @PathVariable("userId") int userId, @PathVariable("code") String code) {
        int result = userService.activation(userId, code);
        if (result == ACTIVATION_SUCCESS) {
            model.addAttribute("msg", "激活成功,您的账号已经可以正常使用了!");
            model.addAttribute("target", "/login");
        } else if (result == ACTIVATION_REPEAT) {
            model.addAttribute("msg", "无效操作,该账号已经激活过了!");
            model.addAttribute("target", "/index");
        } else {
            model.addAttribute("msg", "激活失败,您提供的激活码不正确!");
            model.addAttribute("target", "/index");
        }
        return "/site/operate-result";
    }

login添加到模板

【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、

【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、

验证码更改

【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、

首页更改——index

【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、

激活,跳到登录页面

【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、

三、会话管理

【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、
HTTP教程

HTTP 是无状态,有会话的

HTTP 是无状态的:在同一个连接中,两个执行成功的请求之间是没有关系的。这就带来了一个问题,用户没有办法在同一个网站中进行连续的交互,比如在一个电商网站里,用户把某个商品加入到购物车,切换一个页面后再次添加了商品,这两次添加商品的请求之间没有关联,浏览器无法知道用户最终选择了哪些商品。而使用 HTTP 的头部扩展,HTTP Cookies 就可以解决这个问题。把 Cookies 添加到头部中,创建一个会话让每次请求都能共享相同的上下文信息,达成相同的状态。

1、HTTP Cookie

HTTP Cookie(也叫 Web Cookie 或浏览器 Cookie)是服务器发送到用户浏览器并保存在本地的一小块数据,它会在浏览器下次向同一服务器再发起请求时被携带并发送到服务器上。通常,它用于告知服务端两个请求是否来自同一浏览器,如保持用户的登录状态。Cookie 使基于无状态的 HTTP 协议记录稳定的状态信息成为了可能。

  • 是服务器发送到浏览器,并保存在浏览器端的一小块数据。

  • 浏览器下次访问该服务器时,会自动携带块该数据,将其发送给服务器。

  • 识别浏览器、记住浏览器

  • 下次再发到浏览器,会携带上次数据

好处:弥补HTTP无状态时的情况,让业务得以延续

缺点:

  • 存在客户端,不安全
  • 增加数据量,影响性能

【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、

  • 浏览器访问服务器,服务器会产生一个Cookie对象
  • 服务器-返回——Cookie,其中携带数据(默认在响应的头里),浏览器保存以下数据
  • 浏览器——服务器,Cookie在请求的头里,服务器记住用户

set cookie

    // cookie示例

    @RequestMapping(path = "/cookie/set", method = RequestMethod.GET)
    @ResponseBody
    public String setCookie(HttpServletResponse response) {
        // 创建cookie
        Cookie cookie = new Cookie("code", CommunityUtil.generateUUID());
        // 设置cookie生效的范围
        cookie.setPath("/community/alpha");
        // 设置cookie的生存时间
        cookie.setMaxAge(60 * 10);
        // 发送cookie
        response.addCookie(cookie);

        return "set cookie";
    }

【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、

get cookie

    @RequestMapping(path = "/cookie/get", method = RequestMethod.GET)
    @ResponseBody
    public String getCookie(@CookieValue("code") String code) {
        System.out.println(code);
        return "get cookie";
    }

【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、

2、Session

  • JavaEE的标准,用于在服务端记录客户端信息。
  • 数据存放在服务端更加安全,但是也会增加服务端的内存压力。

服务器靠什么区分Session

  • 服务器——浏览器发送cookiecookie中携带Session标识

【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、

set Session

    // session示例

    @RequestMapping(path = "/session/set", method = RequestMethod.GET)
    @ResponseBody
    public String setSession(HttpSession session) {
        session.setAttribute("id", 1);
        session.setAttribute("name", "Test");
        return "set session";
    }

【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、

get session

    // session示例
    @RequestMapping(path = "/session/get", method = RequestMethod.GET)
    @ResponseBody
    public String getSession(HttpSession session) {
        System.out.println(session.getAttribute("id"));
        System.out.println(session.getAttribute("name"));
        return "get session";
    }

【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、

为什么在分布式部署下,Session用的少了?实际应用中怎么解决

  • 分布式部署——同时部署多台服务器,同时向浏览器提供支持
分布式部署下,有什么问题:

【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、

解决方法

1、粘性Session

每个浏览器始终分配给一台服务器去处理,固定ip给同一个服务器
缺点:难以保证负载均衡,性能不好

2、同步Session

服务器之间同步Session
缺点: 对服务器性能产生影响,服务器之间耦合

3、共享Session

单独有一台服务器来处理Session
缺点:这台服务器挂了都无法工作

4、能存cookie就存cookie,敏感数据可以存到数据库里

【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、

优点:

  • 很好的共享数据、同步数据
    缺点:
  • 传统的关系型数据库把数据存到硬盘,访问数据到硬盘,性能慢
  • 并发量大出现瓶颈

5、 可以存到非关系型数据库 Redis

【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、

目前没部署Redis,怎么办?

  • 适合存到MySQL,就存
  • 不适合存到session

四、生成验证码——Kaptcha

【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、

1、Kaptcha

Kaptcha官方手册

  • 导入jar
  • 编写Kaptcha配置类
  • 生成随机字符、生成图片

【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、

导入依赖

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

2、KaptchaConfig——定义验证码图片

package com.nowcoder.community.config;

import com.google.code.kaptcha.Producer;
import com.google.code.kaptcha.impl.DefaultKaptcha;
import com.google.code.kaptcha.util.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.Properties;

@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;
    }

}

3、LoginController——生成验证码

@Autowired
    private Producer kaptchaProducer;

    private static final Logger logger = LoggerFactory.getLogger(LoginController.class);

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

        // 将验证码存入session
        session.setAttribute("kaptcha", text);

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

【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、

4、刷新验证码

【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、

【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、

【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、

【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、

结果
【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、

5、总结

关于Kaptcha的描述

  • ProducerKaptcha的核心接口
  • DefaultKaptchaKaptcha核心接口的默认实现类
  • Spring Boot没有为Kaptcha提供自动配置

关于使用Kaptcha的描述

  • 可以通过Producer创建随机的验证码文本
  • 可以传入文本,让Producer创建对应的验证码图片
  • 服务端需要将验证码图片输出给浏览器

关于Kaptcha配置的描述

  • 可以配置Kaptcha图片的宽度、高度、字号、颜色
  • 可以配置Kaptcha验证码的字符范围、字符个数

五、开发登录、退出功能

【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、

访问登录页面

  • 点击顶部区域内的链接,打开登录页面。

1、数据库——login_ticket

【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、

  • id——主键
  • user_id——用户id
  • ticket——凭证(唯一标识,唯一字符串)
  • status——0 有效, 1 无效
  • expired——过期时间

2、实体类——LoginTicket

getter and setter
toString

public class LoginTicket {

    private int id;
    private int userId;
    private String ticket;
    private int status;
    private Date expired;
   
}

3、LoginTicketMapper——写SQL、通过注解

依据ticket,来查找

package com.nowcoder.community.dao;

import com.nowcoder.community.entity.LoginTicket;
import org.apache.ibatis.annotations.*;

@Mapper
public interface LoginTicketMapper {

    @Insert({
            "insert into login_ticket(user_id,ticket,status,expired) ",
            "values(#{userId},#{ticket},#{status},#{expired})"
    })
    @Options(useGeneratedKeys = true, keyProperty = "id")  // 希望主键自动生成
    int insertLoginTicket(LoginTicket loginTicket);

    @Select({
            "select id,user_id,ticket,status,expired ",
            "from login_ticket where ticket=#{ticket}"
    })
    // 以 ticket 为凭证查询
    LoginTicket selectByTicket(String ticket);

    @Update({
            "<script>",
            "update login_ticket set status=#{status} where ticket=#{ticket} ",
            "<if test=\"ticket!=null\"> ",
            "and 1=1 ",
            "</if>",
            "</script>"
    })
    int updateStatus(String ticket, int status);

}

4、测试

    @Test
    public void testInsertLoginTicket() {
        LoginTicket loginTicket = new LoginTicket();
        loginTicket.setUserId(101);
        loginTicket.setTicket("abc");
        loginTicket.setStatus(0);
        loginTicket.setExpired(new Date(System.currentTimeMillis() + 1000 * 60 * 10));

        loginTicketMapper.insertLoginTicket(loginTicket);
    }

【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、

    @Test
    public void testSelectLoginTicket() {
        LoginTicket loginTicket = loginTicketMapper.selectByTicket("abc");
        System.out.println(loginTicket);

        loginTicketMapper.updateStatus("abc", 1);
        loginTicket = loginTicketMapper.selectByTicket("abc");
        System.out.println(loginTicket);
    }

【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、

登录

  • 验证账号、密码、验证码。
  • 成功时,生成登录凭证,发放给客户端。
  • 失败时,跳转回登录页。

1、业务层——UserService

登录失败的原因:

  • 账号没输入、不存在、没激活

用户在页面输入的密码是明文

// 用户在页面输入的密码是明文,存的是加密后的,MD5
    // expiredSeconds 多长时间后,凭证过期
    public Map<String, Object> login(String username, String password, int expiredSeconds) {
        Map<String, Object> map = new HashMap<>();

        // 空值处理
        if (StringUtils.isBlank(username)) {
            map.put("usernameMsg", "账号不能为空!");
            return map;
        }
        if (StringUtils.isBlank(password)) {
            map.put("passwordMsg", "密码不能为空!");
            return map;
        }

        // 验证账号
        User user = userMapper.selectByName(username);
        if (user == null) {
            map.put("usernameMsg", "该账号不存在!");
            return map;
        }

        // 验证状态
        if (user.getStatus() == 0) {
            map.put("usernameMsg", "该账号未激活!");
            return map;
        }

        // 验证密码
        password = CommunityUtil.md5(password + user.getSalt());
        if (!user.getPassword().equals(password)) {
            map.put("passwordMsg", "密码不正确!");
            return map;
        }

        // 生成登录凭证
        LoginTicket loginTicket = new LoginTicket();
        loginTicket.setUserId(user.getId());
        loginTicket.setTicket(CommunityUtil.generateUUID());
        loginTicket.setStatus(0);
        loginTicket.setExpired(new Date(System.currentTimeMillis() + expiredSeconds * 1000));
        loginTicketMapper.insertLoginTicket(loginTicket);

        map.put("ticket", loginTicket.getTicket());
        return map;
    }

    public void logout(String ticket) {
        loginTicketMapper.updateStatus(ticket, 1);
    }

2、LoginController

  • 验证账号、密码、验证码。
  • 成功时,生成登录凭证,发放给客户端。
  • 失败时,跳转回登录页。
@RequestMapping(path = "/login", method = RequestMethod.POST)
    public String login(String username, String password, String code, boolean rememberme,
                        Model model, HttpSession session, HttpServletResponse response) {
        // 检查验证码
        String kaptcha = (String) session.getAttribute("kaptcha");
        if (StringUtils.isBlank(kaptcha) || StringUtils.isBlank(code) || !kaptcha.equalsIgnoreCase(code)) {
            model.addAttribute("codeMsg", "验证码不正确!");
            return "/site/login";
        }

        // 检查账号,密码
        int expiredSeconds = rememberme ? REMEMBER_EXPIRED_SECONDS : DEFAULT_EXPIRED_SECONDS;
        Map<String, Object> map = userService.login(username, password, expiredSeconds);
        if (map.containsKey("ticket")) {
            Cookie cookie = new Cookie("ticket", map.get("ticket").toString());
            cookie.setPath(contextPath);    // cookie路径——整个项目
            cookie.setMaxAge(expiredSeconds);       // cookie 有效时间
            response.addCookie(cookie);         // 把 cookie 发送给页面上
            return "redirect:/index";
        } else {
            model.addAttribute("usernameMsg", map.get("usernameMsg"));
            model.addAttribute("passwordMsg", map.get("passwordMsg"));
            return "/site/login";
        }
    }

3、登录页面

发送请求时,修改表单提交方式、路径、名字
【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、

【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、

【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、
错误信息展示,给默认值

  • 账号
  • 密码
    【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、
    【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、
    【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、

账号相关的提示是动态的

退出

  • 将登录凭证修改为失效状态。
  • 跳转至网站首页。

1、状态标识——UserService

  • ticket 改为 1,无效
    public void logout(String ticket) {
        loginTicketMapper.updateStatus(ticket, 1);
    }

2、返回退出页面请求——LoginController

  • 返回重新登录页面
    @RequestMapping(path = "/logout", method = RequestMethod.GET)
    public String logout(@CookieValue("ticket") String ticket) {
        userService.logout(ticket);
        return "redirect:/login";
    }

【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、

【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、

3、配置退出登录页面的链接——index

【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、
【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、

六、显示登录信息

【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、

拦截器

  • 示例定义拦截器,实现Handlerlnterceptor
  • 配置拦截器,为它指定拦截、排除的路径

拦截器应用

  • 在请求开始时查询登录用户。
  • 在本次请求中持有用户数据
  • 在模板视图上显示用户数据
  • 在请求结束时清理用户数据

拦截器可以拦截请求,在拦截请求的开始和结束,插入一些代码

【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、

1、拦截器

  • 示例定义拦截器,实现Handlerlnterceptor
  • 配置拦截器,为它指定拦截、排除的路径

拦截器测试——AlphaInterceptor

【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、

package com.nowcoder.community.controller.interceptor;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Component
public class AlphaInterceptor implements HandlerInterceptor {

    // 日志——debug级别
    private static final Logger logger = LoggerFactory.getLogger(AlphaInterceptor.class);

    // 在Controller之前执行, 请求之前执行
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        logger.debug("preHandle: " + handler.toString());   //debug级别
        return true;
    }

    // 在Controller之后执行,  模板之前执行
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        logger.debug("postHandle: " + handler.toString());
    }

    // 在 TemplateEngine 之后执行
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        logger.debug("afterCompletion: " + handler.toString());
    }
}

配置类——WebMvcConfig

【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、

  • 实现接口——WebMvcConfigurer
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    //拦截器注入
    @Autowired
    private AlphaInterceptor alphaInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        /*
            拦截一切请求,不拦截的 加后边
         */
        //  /**/*.css _ static 目录下
        registry.addInterceptor(alphaInterceptor)
                .excludePathPatterns("/**/*.css", "/**/*.js", "/**/*.png", "/**/*.jpg", "/**/*.jpeg")
                .addPathPatterns("/register", "/login");   // 拦截注册 和登录

    }

2、拦截器应用

  • 在请求开始时查询登录用户。
  • 在本次请求中持有用户数据
  • 在模板视图上显示用户数据
  • 在请求结束时清理用户数据

拦截器——LoginTicketInterceptor

每次请求的过程
【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、

request获取Cookie——CookieUtil
  • 复用request获取Cookie
  • 返回Cookie中的值
public class CookieUtil {

    public static String getValue(HttpServletRequest request, String name) {
        if (request == null || name == null) {
            throw new IllegalArgumentException("参数为空!");
        }

        Cookie[] cookies = request.getCookies();
        if (cookies != null) {
            for (Cookie cookie : cookies) {
                // cookie 的 name 是不是传入的
                if (cookie.getName().equals(name)) {
                    return cookie.getValue();
                }
            }
        }

        return null;
    }

}

查询登录凭证——UserService
    //查询登录凭证
    public LoginTicket findLoginTicket(String ticket) {
        return loginTicketMapper.selectByTicket(ticket);
    }
找map——HostHolder
  • 持有用户信息,用于代替session对象.
/**
 * 持有用户信息,用于代替session对象.
 */
@Component
public class HostHolder {

    private ThreadLocal<User> users = new ThreadLocal<>();

    public void setUser(User user) {
        users.set(user);
    }

    public User getUser() {
        return users.get();
    }

    public void clear() {
        users.remove();
    }

}

拦截器主体代码-LoginTicketInterceptor
@Component
public class LoginTicketInterceptor implements HandlerInterceptor {

    @Autowired
    private UserService userService;

    @Autowired
    private HostHolder hostHolder;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 从 cookie 中获取凭证  cookie——ticket
        String ticket = CookieUtil.getValue(request, "ticket");

        if (ticket != null) {
            // 查询凭证
            LoginTicket loginTicket = userService.findLoginTicket(ticket);
            // 检查凭证是否有效
            // 凭证不为空、状态为 0、超时时间晚于登陆时间
            if (loginTicket != null && loginTicket.getStatus() == 0 && loginTicket.getExpired().after(new Date())) {
                // 根据凭证查询用户
                User user = userService.findUserById(loginTicket.getUserId());
                // 在本次请求中持有用户
                hostHolder.setUser(user);
            }
        }

        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        User user = hostHolder.getUser();
        if (user != null && modelAndView != null) {
            // 将 user 添加到 model
            modelAndView.addObject("loginUser", user);
        }
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        // 清理数据
        hostHolder.clear();
    }
}

配置——WebMvcConfig
  @Autowired
    private LoginTicketInterceptor loginTicketInterceptor;

	  @Override
    public void addInterceptors(InterceptorRegistry registry) {
        /*
            拦截一切请求,不拦截的 加后边
         */
        registry.addInterceptor(loginTicketInterceptor)
                .excludePathPatterns("/**/*.css", "/**/*.js", "/**/*.png", "/**/*.jpg", "/**/*.jpeg");
    }

首页——index

登录 才能看到 消息

【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、

没登录 才显示 注册

【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、

没登录 才显示 登录

【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、

调整登录账号显示

【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、

【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、

3、运行结果:

【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、

【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、

【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、

4、总结

关于Spring MVC拦截器:

  • 拦截器需实现HandlerInterceptor接口,而WebMvcConfigurer接口是MVC配置类要实现的接口
  • preHandle方法在Controller之前执行,若返回false,则终止执行后续的请求。
  • postHandle方法在Controller之后、模板之前执行。
  • afterCompletion方法在模板之后执行。

关于配置Spring MVC拦截器

  • 配置类需实现WebMvcConfigurer接口
  • 通过addInterceptors方法对拦截器进行配置
  • 可以配置忽略拦截的路径,也可以配置希望拦截的路径

关于ThreadLocal的描述

  • ThreadLocal采用线程隔离的方式存放数据,可以避免多线程之间出现数据访问冲突。
  • ThreadLocal提供set方法,能够以当前线程为key存放数据。
  • ThreadLocal提供get方法,能够以当前线程为key获取数据。

七、账号设置——上传头像、修改密码

【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、
上传文件

  • 请求:必须是POST请求
  • 表单:enctype="multipart/form-data'
  • Spring MVC:通过MultipartFile处理上传文件

开发步骤

  • 访问账号设置页面
  • 上传头像
  • 获取头像

完成这个页面
【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、

1、可以访问这个页面

返回访问页面——UserController

【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、

    @RequestMapping(path = "/setting", method = RequestMethod.GET)
    public String getSettingPage() {
        return "/site/setting";
    }

显示页面——setting.html

修改路径等
【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、
【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、
【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、

index中修改——链接

【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、

2、上传头像

  • 开放时,是Windows
  • 上线时,是 Linux

配置文件

添加 存储 上传文件的路径

community.path.upload=j:/work/data/upload

UserService

更新修改图像的路径,返回更新行数

    public int updateHeader(int userId, String headerUrl) {
        return userMapper.updateHeader(userId, headerUrl);
    }

UserController

  • 上传表单提交为post
    请求
  • 项目域名
  • 项目名

	private static final Logger logger = LoggerFactory.getLogger(UserController.class);

    @Value("${community.path.upload}")
    private String uploadPath;    // 上传路径

    @Value("${community.path.domain}")
    private String domain;      // 域名

    @Value("${server.servlet.context-path}")
    private String contextPath;     //项目名

    @Autowired
    private UserService userService;

    @Autowired
    private HostHolder hostHolder;      //取 当前用户是谁
    
    @RequestMapping(path = "/upload", method = RequestMethod.POST)
    public String uploadHeader(MultipartFile headerImage, Model model) {
        if (headerImage == null) {
            model.addAttribute("error", "您还没有选择图片!");
            return "/site/setting";
        }

        String fileName = headerImage.getOriginalFilename();   // 读取文件的后缀
        String suffix = fileName.substring(fileName.lastIndexOf("."));      // 截取后缀
        if (StringUtils.isBlank(suffix)) {
            model.addAttribute("error", "文件的格式不正确!");
            return "/site/setting";
        }

        // 生成随机文件名
        fileName = CommunityUtil.generateUUID() + suffix;
        // 确定文件存放的路径
        File dest = new File(uploadPath + "/" + fileName);
        try {
            // 存储文件
            headerImage.transferTo(dest);
        } catch (IOException e) {
            logger.error("上传文件失败: " + e.getMessage());
            throw new RuntimeException("上传文件失败,服务器发生异常!", e);
        }

        // 更新当前用户的头像的路径(web访问路径)
        // http://localhost:8080/community/user/header/xxx.png
        User user = hostHolder.getUser();
        String headerUrl = domain + contextPath + "/user/header/" + fileName;
        userService.updateHeader(user.getId(), headerUrl);

        return "redirect:/index";
    }

    @RequestMapping(path = "/header/{fileName}", method = RequestMethod.GET)
    public void getHeader(@PathVariable("fileName") String fileName, HttpServletResponse response) {
        // 服务器存放路径
        fileName = uploadPath + "/" + fileName;
        // 文件后缀
        String suffix = fileName.substring(fileName.lastIndexOf("."));
        // 响应图片
        response.setContentType("image/" + suffix);
        try (
                FileInputStream fis = new FileInputStream(fileName);
                OutputStream os = response.getOutputStream();
        ) {
            byte[] buffer = new byte[1024];
            int b = 0;
            while ((b = fis.read(buffer)) != -1) {
                os.write(buffer, 0, b);
            }
        } catch (IOException e) {
            logger.error("读取头像失败: " + e.getMessage());
        }
    }

账号设置——setting.html

【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、
【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、

3、修改密码

UserService

    public User findUserByName(String username) {
        return userMapper.selectByName(username);
    }

    // 重置密码
    public Map<String, Object> resetPassword(String email, String password) {
        Map<String, Object> map = new HashMap<>();

        // 空值处理
        if (StringUtils.isBlank(email)) {
            map.put("emailMsg", "邮箱不能为空!");
            return map;
        }
        if (StringUtils.isBlank(password)) {
            map.put("passwordMsg", "密码不能为空!");
            return map;
        }

        // 验证邮箱
        User user = userMapper.selectByEmail(email);
        if (user == null) {
            map.put("emailMsg", "该邮箱尚未注册!");
            return map;
        }

        // 重置密码
        password = CommunityUtil.md5(password + user.getSalt());
        userMapper.updatePassword(user.getId(), password);

        map.put("user", user);
        return map;
    }

    // 修改密码
    public Map<String, Object> updatePassword(int userId, String oldPassword, String newPassword) {
        Map<String, Object> map = new HashMap<>();

        // 空值处理
        if (StringUtils.isBlank(oldPassword)) {
            map.put("oldPasswordMsg", "原密码不能为空!");
            return map;
        }
        if (StringUtils.isBlank(newPassword)) {
            map.put("newPasswordMsg", "新密码不能为空!");
            return map;
        }

        // 验证原始密码
        User user = userMapper.selectById(userId);
        oldPassword = CommunityUtil.md5(oldPassword + user.getSalt());
        if (!user.getPassword().equals(oldPassword)) {
            map.put("oldPasswordMsg", "原密码输入有误!");
            return map;
        }

        // 更新密码
        newPassword = CommunityUtil.md5(newPassword + user.getSalt());
        userMapper.updatePassword(userId, newPassword);

        return map;
    }

UserController

    // 修改密码
    @RequestMapping(path = "/updatePassword", method = RequestMethod.POST)
    public String updatePassword(String oldPassword, String newPassword, Model model) {
        User user = hostHolder.getUser();
        Map<String, Object> map = userService.updatePassword(user.getId(), oldPassword, newPassword);
        if (map == null || map.isEmpty()) {
            return "redirect:/logout";
        } else {
            model.addAttribute("oldPasswordMsg", map.get("oldPasswordMsg"));
            model.addAttribute("newPasswordMsg", map.get("newPasswordMsg"));
            return "/site/setting";
        }
    }

setting.html

更改

总结

上传文件的必要条件

  • 必须在POST请求中上传文件
  • 表单的enctype属性必须设置为“multipart/form-data”

关于上传路径与访问路径的描述

  • 上传路径可以是本地路径也可以是web路径,
  • 访问路径必须是符合HTTP协议的Web路径。

关于MultipartFile类型的描述

  • 一个MultipartFile只能封装一个文件
  • 通过MultipartFilegetOriginalFilename方法,可以获得原始文件名
  • 通过MultipartFiletransferTo方法,可以将文件存入指定位置

八、检查登录状态

【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、

  • 没有登陆也能访问登录后的页面
  • 安全隐患
  • 拦截器——不在配置文件中拦截,用注解在方法上拦截

使用拦截器

  • 在方法前标注自定义注解
  • 拦截所有请求,只处理带有该注解的方法

自定义注解

常用的元注解:

  • @Target、——声明自定义注解作用在哪个位置,例如方法上、类上
  • @Retention、——声明自定义注解的有效时间(编译时、运行时)
  • @Document、——声明自定义注解生成文档的时候要不要把注解带上去
  • @Inherited——用于继承,父类有注解,子类是否要继承

如何读取注解:

  • 反射
  • Method.getDeclaredAnnotations()
  • Method.getAnnotation(class<T>annotationclass)

1、写注解——LoginRequired

新建一个包,写注解
【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、

【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、

@Target(ElementType.METHOD)   // 方法上
@Retention(RetentionPolicy.RUNTIME)  //程序运行时有效
public @interface LoginRequired {

        // 里边不用写内容,标注解就行
}

2、加上注解——UserController

在需要的方法上,加上注解

【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、

3、拦截器——LoginRequiredInterceptor

【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、

@Component
public class LoginRequiredInterceptor implements HandlerInterceptor {

    @Autowired
    private HostHolder hostHolder;   // 获取当前用户

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 判断拦截到的是不是方法
        if (handler instanceof HandlerMethod) {
            HandlerMethod handlerMethod = (HandlerMethod) handler;  // 转型
            Method method = handlerMethod.getMethod();  // 获得方法
            LoginRequired loginRequired = method.getAnnotation(LoginRequired.class); // 从方法里取注解
            // 当前方法需要登录,但是用户没登录
            if (loginRequired != null && hostHolder.getUser() == null) {
                // 重定向 —— 项目名
                response.sendRedirect(request.getContextPath() + "/login");
                return false;  // 拒绝请求
            }
        }
        return true;
    }
}

4、拦截器配置——WebMvcConfig

拦截器配置——指定生成的路径

好处,拦截谁,就给谁加注解

  // 拦截指定方法
    @Autowired
    private LoginRequiredInterceptor loginRequiredInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        /*
            拦截一切请求,不拦截的 加后边
         */
        //  /**/*.css _ static 目录下

        registry.addInterceptor(loginRequiredInterceptor)
                .excludePathPatterns("/**/*.css", "/**/*.js", "/**/*.png", "/**/*.jpg", "/**/*.jpeg");

    }

总结

关于元注解

  • @Target用于描述该注解可以作用的目标类型
  • @Retention用于描述该注解被保留的时间
  • @Document用于描述该注解是否可以生成到文档里
  • 比如LoginRequired加上了这个@Inherited,那注解LoginRequired的类的子类也会自动注解上LoginRequired

关于解析注解

  • 在程序中,可以通过反射的方式解析注解
  • 通过Method对象可以获取某方法上标注的所有注解
  • 通过Method对象可以获取某方法上指定类型的注解
  • Method对象上还有很多其他的方法,可以获取该方法上标注的注解

在程序中,可以通过哪些方式正确实现重定向文章来源地址https://www.toymoban.com/news/detail-462018.html

  • 在Controller的方法里,通过返回以”redirect”开头的字符串实现重定向
  • 在Controller的方法里,通过response对象的sendRedirect方法实现重定向
  • 在拦截器中,通过response对象的sendRedirect方法实现重定向

到了这里,关于【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • (SpringBoot)第二章:Spring创建和使用

    注意 :在Java中对象也叫做Bean,所以后续文章中用Be

    2024年02月08日
    浏览(24)
  • 头歌实践教学平台Python-Python第二章作业(初级)

    第1关:三角形周长及面积 任务描述 输入的三角形的三条边a、b、c 的长度,计算并依次输出三角形的周长和面积,结果严格保留2位小数。测试用例的数据保证三角形三边数据可以构成三角形。 三角形面积计算公式: ,其中s=(a+b+c)/2。  第2关:三角函数计算 根据下面公式 计

    2024年02月08日
    浏览(112)
  • k8s学习 — (实践)第二章 搭建k8s集群

    k8s学习 — 各章节重要知识点 推荐学习时使用,轻量化的k8s集群,可以在个人电脑上使用。 minikube 是一个工具, 能让你在本地运行 Kubernetes。 minikube 在你的个人计算机(包括 Windows、macOS 和 Linux PC)上运行一个一体化(all-in-one)或多节点的本地 Kubernetes 集群,以便你来尝试

    2024年02月03日
    浏览(26)
  • [第二章—Spring MVC的高级技术] 2.3 处理异常

    各位小猿,程序员小猿开发笔记,希望大家共同进步。 引言 我是谁——异常处理。 来自那——所有功能正常运行,但出现错误 怎么办——如何处理异常和响应客户端 我是谁——Spring框架中的一个注解 用在哪——应用在控制器类或方法上 什么用——用于在控制器方法中指定

    2024年01月22日
    浏览(25)
  • 【Java入门合集】第二章Java语言基础(一)

    博主:命运之光 专栏:Java零基础入门 学习目标 掌握变量、常量、表达式的概念,数据类型及变量的定义方法; 掌握常用运算符的使用; 掌握程序的顺序结构、选择结构和循环结构的使用; 掌握数组的定义及使用方法; 掌握基本的输入输出方法; 提示:不要去强记

    2024年02月02日
    浏览(20)
  • 【Java入门合集】第二章Java语言基础(三)

    博主:命运之光 专栏:Java零基础入门 学习目标 掌握变量、常量、表达式的概念,数据类型及变量的定义方法; 掌握常用运算符的使用; 掌握程序的顺序结构、选择结构和循环结构的使用; 掌握数组的定义及使用方法; 掌握基本的输入输出方法; Java中的语句有很多种形式

    2024年02月03日
    浏览(23)
  • 【软考高级信息系统项目管理师--第二章:信息技术发展】

    🚀 作者 :“码上有前” 🚀 文章简介 :软考高级–信息系统项目管理师 🚀 欢迎小伙伴们 点赞👍、收藏⭐、留言💬 网络标准协议 OSI七层 物理层(RS232、V.35、RJ-45、FDDI) 数据链路层(IEEE802.3/.2、HDLC、PPP、ATM) 网络层(IP、ICMP、IGMP、IPX、ARP【IP】】) 传输层(TCP、UDP、SPX) 会话层

    2024年02月21日
    浏览(24)
  • 第二章--Java多线程高并发+面试题

    s1 基础知识 001 并发编程的优缺点 优点 充分利用多核cpu的计算能力 方便业务拆分,提升系统并发能力和性能,提升程序执行效率,提速 缺点 内存泄漏 | 上下文切换 线程安全 | 死锁 002 并发编程三要素 三要素 安全问题的原因 具体原因 对策 原子性 线程切换 一个或多个操作要么

    2024年02月06日
    浏览(33)
  • 第二章:项目环境搭建【基于Servlet+JSP的图书管理系统】

    02-图书管理系统-项目环境搭建   本项目涉及到的工具都有在云盘提供,自行下载即可 JDK8 IDEA2021 Tomcat8.5 MySQL的客户端工具SQLYog …   通过IDEA创建maven项目。勾选脚手架工具。选择 maven-archetype-webapp 设置项目的基础信息 3.1 JDK配置   JDK使用的是JDK8。我们也需要配置下:

    2024年02月11日
    浏览(23)
  • 第二章 02Java基础-数据类型、标识符、键盘录入

    今天我们学习Java基础,数据类型、标识符、键盘录入 1.数据类型大体上可以分为两类,一类是基本数据类型,另外一类是引用数据类型。今天我们学习基本数据类型。 2.基本数据类型可以分为四类八种,整数(byte short int long)、浮点数(float double)、字符(char)和布尔(

    2024年02月06日
    浏览(22)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包