国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

【論壇java項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、

這篇具有很好參考價(jià)值的文章主要介紹了【論壇java項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

??如果對(duì)你有幫助的話??
??為博主點(diǎn)個(gè)贊吧 ??
??點(diǎn)贊是對(duì)博主最大的鼓勵(lì)??
??愛心發(fā)射~??

【論壇java項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、

一、發(fā)送郵件

【論壇java項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、

1、啟用客戶端SMTP服務(wù)

【論壇java項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、

【論壇java項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、
bofryuzursekbiab——密碼

2、導(dǎo)入jar包

【論壇java項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、

<!-- 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、郵箱參數(shù)配置

  • 訪問郵箱域名
  • 郵箱端口
  • 賬號(hào)
  • 密碼
  • 協(xié)議
  • 詳細(xì)配置
# 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項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、

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("發(fā)送郵件失敗:" + 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);
    }

}

總結(jié)

  1. JavaMailSenderSpring Email的核心組件,負(fù)責(zé)發(fā)送郵件
  2. MimeMessage用于封裝郵件的相關(guān)信息
  3. MimeMessageHelper用于輔助構(gòu)建MimeMessage對(duì)象
  4. TemplateEngine是模板引擎,負(fù)責(zé)格式化HTML格式的郵件

Spring Boot對(duì)發(fā)送郵件提供了支持,可以通過MailProperties對(duì)郵件進(jìn)行配置

  • 可以配置郵件服務(wù)器的域名和端口
  • 可以配置發(fā)件人的賬號(hào)及密碼
  • 可以配置發(fā)送郵件的協(xié)議類型

哪些會(huì)被Spring Boot自動(dòng)裝配到Spring容器中

  • JavaMailSender
  • TemplateEngine

二、開發(fā)注冊(cè)功能

【論壇java項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、

1、訪問注冊(cè)頁面

點(diǎn)擊頂部區(qū)域內(nèi)的鏈接,打開注冊(cè)頁面。

修改——thymeleaf

【論壇java項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、

【論壇java項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、

首頁—超鏈接——index.html

【論壇java項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、

每個(gè)html頭部復(fù)用——index.html

【論壇java項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、

LoginController

【論壇java項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、
返回注冊(cè)頁面

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

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

2、提交注冊(cè)數(shù)據(jù)

通過表單提交數(shù)據(jù)。

添加依賴和配置

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

service層

  • 服務(wù)端發(fā)送激活郵件。
工具類——CommunityUtil

生成隨機(jī)字符串

給文件生成隨機(jī)名字
【論壇java項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、

public class CommunityUtil {

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

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


}

注入——UserService

【論壇java項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、

  • 注入郵件客戶端
  • 注入模板引擎
@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;   // 項(xiàng)目名

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

    public Map<String, Object> register(User user){
        Map<String, Object> map = new HashMap<>();  // map實(shí)例化
        // 空值處理
        if (user == null) {
            throw new IllegalArgumentException("參數(shù)不能為空!");
        }
        if (StringUtils.isBlank(user.getUsername())) {
            map.put("usernameMsg", "賬號(hào)不能為空!");
            return map;
        }
        if (StringUtils.isBlank(user.getPassword())) {
            map.put("passwordMsg", "密碼不能為空!");
            return map;
        }
        if (StringUtils.isBlank(user.getEmail())) {
            map.put("emailMsg", "郵箱不能為空!");
            return map;
        }

        // 驗(yàn)證賬號(hào)
        User u = userMapper.selectByName(user.getUsername());
        if (u != null) {
            map.put("usernameMsg", "該賬號(hào)已存在!");
            return map;
        }

        // 驗(yàn)證郵箱
        u = userMapper.selectByEmail(user.getEmail());
        if (u != null) {
            map.put("emailMsg", "該郵箱已被注冊(cè)!");
            return map;
        }

        // 注冊(cè)用戶
        user.setSalt(CommunityUtil.generateUUID().substring(0, 5));    //生成隨機(jī)字符串
        user.setPassword(CommunityUtil.md5(user.getPassword() + user.getSalt()));   //密碼拼接
        user.setType(0);    // 類型
        user.setStatus(0);  //狀態(tài)
        user.setActivationCode(CommunityUtil.generateUUID());   //激活碼
        user.setHeaderUrl(String.format("http://images.nowcoder.com/head/%dt.png", new Random().nextInt(1000)));   //頭像
        user.setCreateTime(new Date());     //  創(chuàng)建時(shí)間
        userMapper.insertUser(user);    //添加庫里

        // 激活郵件
        Context context = new Context();
        context.setVariable("email", user.getEmail());
        // http://localhost:8081/community/activation/101/code
        // 域名——項(xiàng)目名——功能訪問名 + 用戶 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(), "激活賬號(hào)", content);      // 標(biāo)題 內(nèi)容

        return map;
    }
}

牛客網(wǎng)隨機(jī)頭像
【論壇java項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、

改造模板——activation.html

【論壇java項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、

<!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>??途W(wǎng)-激活賬號(hào)</title>
</head>
<body>
<div>
	<p>
		<b th:text="${email}">xxx@xxx.com</b>, 您好!
	</p>
	<p>
		您正在注冊(cè)??途W(wǎng), 這是一封激活郵件, 請(qǐng)點(diǎn)擊
		<a th:href="${url}">此鏈接</a>,
		激活您的??唾~號(hào)!
	</p>
</div>
</body>
</html>

控制層

  • 服務(wù)端驗(yàn)證賬號(hào)是否已存在、郵箱是否已注冊(cè)。

【論壇java項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、

  • 注冊(cè)成功——到首頁進(jìn)行激活——在登陸

@{}:路徑是動(dòng)態(tài)的
${}:里邊是變量

注冊(cè)成功或有錯(cuò)誤返回——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", "注冊(cè)成功,我們已經(jīng)向您的郵箱發(fā)送了一封激活郵件,請(qǐng)盡快激活!");
            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
賬號(hào)、密碼、郵箱錯(cuò)誤——返回register.html

默認(rèn)值的顯示
【論壇java項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、
【論壇java項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、


【論壇java項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、

3、激活注冊(cè)賬號(hào)

點(diǎn)擊郵件中的鏈接,訪問服務(wù)端的激活服務(wù)。

在service層加一個(gè)業(yè)務(wù),幾種情況:

  • 激活成功
  • 多次點(diǎn)擊激活鏈接
  • 重復(fù)激活給提示
  • 激活碼偽造

三種結(jié)果:

  • 成功
  • 重復(fù)激活
  • 失敗

常量接口——CommunityConstant

【論壇java項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、

public interface CommunityConstant {

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

    /**
     * 重復(fù)激活
     */
    int ACTIVATION_REPEAT = 1;

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

}

UserService

    public int activation(int userId, String code) {
        User user = userMapper.selectById(userId);
        // 看狀態(tài)、 激活碼
        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";
    }
    //處理請(qǐng)求
    // 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", "激活成功,您的賬號(hào)已經(jīng)可以正常使用了!");
            model.addAttribute("target", "/login");
        } else if (result == ACTIVATION_REPEAT) {
            model.addAttribute("msg", "無效操作,該賬號(hào)已經(jīng)激活過了!");
            model.addAttribute("target", "/index");
        } else {
            model.addAttribute("msg", "激活失敗,您提供的激活碼不正確!");
            model.addAttribute("target", "/index");
        }
        return "/site/operate-result";
    }

login添加到模板

【論壇java項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、

【論壇java項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、

驗(yàn)證碼更改

【論壇java項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、

首頁更改——index

【論壇java項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、

激活,跳到登錄頁面

【論壇java項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、

三、會(huì)話管理

【論壇java項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、
HTTP教程

HTTP 是無狀態(tài),有會(huì)話的

HTTP 是無狀態(tài)的:在同一個(gè)連接中,兩個(gè)執(zhí)行成功的請(qǐng)求之間是沒有關(guān)系的。這就帶來了一個(gè)問題,用戶沒有辦法在同一個(gè)網(wǎng)站中進(jìn)行連續(xù)的交互,比如在一個(gè)電商網(wǎng)站里,用戶把某個(gè)商品加入到購物車,切換一個(gè)頁面后再次添加了商品,這兩次添加商品的請(qǐng)求之間沒有關(guān)聯(lián),瀏覽器無法知道用戶最終選擇了哪些商品。而使用 HTTP 的頭部擴(kuò)展,HTTP Cookies 就可以解決這個(gè)問題。把 Cookies 添加到頭部中,創(chuàng)建一個(gè)會(huì)話讓每次請(qǐng)求都能共享相同的上下文信息,達(dá)成相同的狀態(tài)。

1、HTTP Cookie

HTTP Cookie(也叫 Web Cookie 或?yàn)g覽器 Cookie)是服務(wù)器發(fā)送到用戶瀏覽器并保存在本地的一小塊數(shù)據(jù),它會(huì)在瀏覽器下次向同一服務(wù)器再發(fā)起請(qǐng)求時(shí)被攜帶并發(fā)送到服務(wù)器上。通常,它用于告知服務(wù)端兩個(gè)請(qǐng)求是否來自同一瀏覽器,如保持用戶的登錄狀態(tài)。Cookie 使基于無狀態(tài)的 HTTP 協(xié)議記錄穩(wěn)定的狀態(tài)信息成為了可能。

  • 是服務(wù)器發(fā)送到瀏覽器,并保存在瀏覽器端的一小塊數(shù)據(jù)。

  • 瀏覽器下次訪問該服務(wù)器時(shí),會(huì)自動(dòng)攜帶塊該數(shù)據(jù),將其發(fā)送給服務(wù)器。

  • 識(shí)別瀏覽器、記住瀏覽器

  • 下次再發(fā)到瀏覽器,會(huì)攜帶上次數(shù)據(jù)

好處:彌補(bǔ)HTTP無狀態(tài)時(shí)的情況,讓業(yè)務(wù)得以延續(xù)

缺點(diǎn):

  • 存在客戶端,不安全
  • 增加數(shù)據(jù)量,影響性能

【論壇java項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、

  • 瀏覽器訪問服務(wù)器,服務(wù)器會(huì)產(chǎn)生一個(gè)Cookie對(duì)象
  • 服務(wù)器-返回——Cookie,其中攜帶數(shù)據(jù)(默認(rèn)在響應(yīng)的頭里),瀏覽器保存以下數(shù)據(jù)
  • 瀏覽器——服務(wù)器,Cookie在請(qǐng)求的頭里,服務(wù)器記住用戶

set cookie

    // cookie示例

    @RequestMapping(path = "/cookie/set", method = RequestMethod.GET)
    @ResponseBody
    public String setCookie(HttpServletResponse response) {
        // 創(chuàng)建cookie
        Cookie cookie = new Cookie("code", CommunityUtil.generateUUID());
        // 設(shè)置cookie生效的范圍
        cookie.setPath("/community/alpha");
        // 設(shè)置cookie的生存時(shí)間
        cookie.setMaxAge(60 * 10);
        // 發(fā)送cookie
        response.addCookie(cookie);

        return "set cookie";
    }

【論壇java項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、

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項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、

2、Session

  • JavaEE的標(biāo)準(zhǔn),用于在服務(wù)端記錄客戶端信息。
  • 數(shù)據(jù)存放在服務(wù)端更加安全,但是也會(huì)增加服務(wù)端的內(nèi)存壓力。

服務(wù)器靠什么區(qū)分Session

  • 服務(wù)器——瀏覽器發(fā)送cookie,cookie中攜帶Session標(biāo)識(shí)

【論壇java項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、

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項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、

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項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、

為什么在分布式部署下,Session用的少了?實(shí)際應(yīng)用中怎么解決

  • 分布式部署——同時(shí)部署多臺(tái)服務(wù)器,同時(shí)向?yàn)g覽器提供支持
分布式部署下,有什么問題:

【論壇java項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、

解決方法

1、粘性Session

每個(gè)瀏覽器始終分配給一臺(tái)服務(wù)器去處理,固定ip給同一個(gè)服務(wù)器
缺點(diǎn):難以保證負(fù)載均衡,性能不好

2、同步Session

服務(wù)器之間同步Session
缺點(diǎn): 對(duì)服務(wù)器性能產(chǎn)生影響,服務(wù)器之間耦合

3、共享Session

單獨(dú)有一臺(tái)服務(wù)器來處理Session
缺點(diǎn):這臺(tái)服務(wù)器掛了都無法工作

4、能存cookie就存cookie,敏感數(shù)據(jù)可以存到數(shù)據(jù)庫里

【論壇java項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、

優(yōu)點(diǎn):

  • 很好的共享數(shù)據(jù)、同步數(shù)據(jù)
    缺點(diǎn):
  • 傳統(tǒng)的關(guān)系型數(shù)據(jù)庫把數(shù)據(jù)存到硬盤,訪問數(shù)據(jù)到硬盤,性能慢
  • 并發(fā)量大出現(xiàn)瓶頸

5、 可以存到非關(guān)系型數(shù)據(jù)庫 Redis

【論壇java項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、

目前沒部署Redis,怎么辦?

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

四、生成驗(yàn)證碼——Kaptcha

【論壇java項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、

1、Kaptcha

Kaptcha官方手冊(cè)

  • 導(dǎo)入jar
  • 編寫Kaptcha配置類
  • 生成隨機(jī)字符、生成圖片

【論壇java項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、

導(dǎo)入依賴

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

2、KaptchaConfig——定義驗(yàn)證碼圖片

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");      // 字號(hào)
        properties.setProperty("kaptcha.textproducer.font.color", "0,0,0");      // 顏色- 黑色
        properties.setProperty("kaptcha.textproducer.char.string", "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYAZ");      // 隨機(jī)字符范圍
        properties.setProperty("kaptcha.textproducer.char.length", "4");      // 長(zhǎng)度
        properties.setProperty("kaptcha.noise.impl", "com.google.code.kaptcha.impl.NoNoise");

        DefaultKaptcha kaptcha = new DefaultKaptcha();  // 默認(rèn)實(shí)現(xiàn)類
        Config config = new Config(properties);   // 配置
        kaptcha.setConfig(config);
        return kaptcha;
    }

}

3、LoginController——生成驗(yàn)證碼

@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) {
        // 生成驗(yàn)證碼
        String text = kaptchaProducer.createText();
        BufferedImage image = kaptchaProducer.createImage(text);

        // 將驗(yàn)證碼存入session
        session.setAttribute("kaptcha", text);

        // 將突圖片輸出給瀏覽器
        response.setContentType("image/png");
        try {
            OutputStream os = response.getOutputStream();
            ImageIO.write(image, "png", os);
        } catch (IOException e) {
            logger.error("響應(yīng)驗(yàn)證碼失敗:" + e.getMessage());
        }
    }

【論壇java項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、

4、刷新驗(yàn)證碼

【論壇java項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、

【論壇java項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、

【論壇java項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、

【論壇java項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、

結(jié)果
【論壇java項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、

5、總結(jié)

關(guān)于Kaptcha的描述

  • ProducerKaptcha的核心接口
  • DefaultKaptchaKaptcha核心接口的默認(rèn)實(shí)現(xiàn)類
  • Spring Boot沒有為Kaptcha提供自動(dòng)配置

關(guān)于使用Kaptcha的描述

  • 可以通過Producer創(chuàng)建隨機(jī)的驗(yàn)證碼文本
  • 可以傳入文本,讓Producer創(chuàng)建對(duì)應(yīng)的驗(yàn)證碼圖片
  • 服務(wù)端需要將驗(yàn)證碼圖片輸出給瀏覽器

關(guān)于Kaptcha配置的描述

  • 可以配置Kaptcha圖片的寬度、高度、字號(hào)、顏色
  • 可以配置Kaptcha驗(yàn)證碼的字符范圍、字符個(gè)數(shù)

五、開發(fā)登錄、退出功能

【論壇java項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、

訪問登錄頁面

  • 點(diǎn)擊頂部區(qū)域內(nèi)的鏈接,打開登錄頁面。

1、數(shù)據(jù)庫——login_ticket

【論壇java項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、

  • id——主鍵
  • user_id——用戶id
  • ticket——憑證(唯一標(biāo)識(shí),唯一字符串)
  • status——0 有效, 1 無效
  • expired——過期時(shí)間

2、實(shí)體類——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、通過注解

依據(jù)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")  // 希望主鍵自動(dòng)生成
    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、測(cè)試

    @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項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、

    @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項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、

登錄

  • 驗(yàn)證賬號(hào)、密碼、驗(yàn)證碼。
  • 成功時(shí),生成登錄憑證,發(fā)放給客戶端。
  • 失敗時(shí),跳轉(zhuǎn)回登錄頁。

1、業(yè)務(wù)層——UserService

登錄失敗的原因:

  • 賬號(hào)沒輸入、不存在、沒激活

用戶在頁面輸入的密碼是明文

// 用戶在頁面輸入的密碼是明文,存的是加密后的,MD5
    // expiredSeconds 多長(zhǎng)時(shí)間后,憑證過期
    public Map<String, Object> login(String username, String password, int expiredSeconds) {
        Map<String, Object> map = new HashMap<>();

        // 空值處理
        if (StringUtils.isBlank(username)) {
            map.put("usernameMsg", "賬號(hào)不能為空!");
            return map;
        }
        if (StringUtils.isBlank(password)) {
            map.put("passwordMsg", "密碼不能為空!");
            return map;
        }

        // 驗(yàn)證賬號(hào)
        User user = userMapper.selectByName(username);
        if (user == null) {
            map.put("usernameMsg", "該賬號(hào)不存在!");
            return map;
        }

        // 驗(yàn)證狀態(tài)
        if (user.getStatus() == 0) {
            map.put("usernameMsg", "該賬號(hào)未激活!");
            return map;
        }

        // 驗(yàn)證密碼
        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

  • 驗(yàn)證賬號(hào)、密碼、驗(yàn)證碼。
  • 成功時(shí),生成登錄憑證,發(fā)放給客戶端。
  • 失敗時(shí),跳轉(zhuǎn)回登錄頁。
@RequestMapping(path = "/login", method = RequestMethod.POST)
    public String login(String username, String password, String code, boolean rememberme,
                        Model model, HttpSession session, HttpServletResponse response) {
        // 檢查驗(yàn)證碼
        String kaptcha = (String) session.getAttribute("kaptcha");
        if (StringUtils.isBlank(kaptcha) || StringUtils.isBlank(code) || !kaptcha.equalsIgnoreCase(code)) {
            model.addAttribute("codeMsg", "驗(yàn)證碼不正確!");
            return "/site/login";
        }

        // 檢查賬號(hào),密碼
        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路徑——整個(gè)項(xiàng)目
            cookie.setMaxAge(expiredSeconds);       // cookie 有效時(shí)間
            response.addCookie(cookie);         // 把 cookie 發(fā)送給頁面上
            return "redirect:/index";
        } else {
            model.addAttribute("usernameMsg", map.get("usernameMsg"));
            model.addAttribute("passwordMsg", map.get("passwordMsg"));
            return "/site/login";
        }
    }

3、登錄頁面

發(fā)送請(qǐng)求時(shí),修改表單提交方式、路徑、名字
【論壇java項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、

【論壇java項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、

【論壇java項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、
錯(cuò)誤信息展示,給默認(rèn)值

  • 賬號(hào)
  • 密碼
    【論壇java項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、
    【論壇java項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、
    【論壇java項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、

賬號(hào)相關(guān)的提示是動(dòng)態(tài)的

退出

  • 將登錄憑證修改為失效狀態(tài)。
  • 跳轉(zhuǎn)至網(wǎng)站首頁。

1、狀態(tài)標(biāo)識(shí)——UserService

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

2、返回退出頁面請(qǐng)求——LoginController

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

【論壇java項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、

【論壇java項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、

3、配置退出登錄頁面的鏈接——index

【論壇java項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、
【論壇java項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、

六、顯示登錄信息

【論壇java項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、

攔截器

  • 示例定義攔截器,實(shí)現(xiàn)Handlerlnterceptor
  • 配置攔截器,為它指定攔截、排除的路徑

攔截器應(yīng)用

  • 在請(qǐng)求開始時(shí)查詢登錄用戶。
  • 在本次請(qǐng)求中持有用戶數(shù)據(jù)
  • 在模板視圖上顯示用戶數(shù)據(jù)
  • 在請(qǐng)求結(jié)束時(shí)清理用戶數(shù)據(jù)

攔截器可以攔截請(qǐng)求,在攔截請(qǐng)求的開始和結(jié)束,插入一些代碼

【論壇java項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、

1、攔截器

  • 示例定義攔截器,實(shí)現(xiàn)Handlerlnterceptor
  • 配置攔截器,為它指定攔截、排除的路徑

攔截器測(cè)試——AlphaInterceptor

【論壇java項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、

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級(jí)別
    private static final Logger logger = LoggerFactory.getLogger(AlphaInterceptor.class);

    // 在Controller之前執(zhí)行, 請(qǐng)求之前執(zhí)行
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        logger.debug("preHandle: " + handler.toString());   //debug級(jí)別
        return true;
    }

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

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

配置類——WebMvcConfig

【論壇java項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、

  • 實(shí)現(xiàn)接口——WebMvcConfigurer
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

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

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        /*
            攔截一切請(qǐng)求,不攔截的 加后邊
         */
        //  /**/*.css _ static 目錄下
        registry.addInterceptor(alphaInterceptor)
                .excludePathPatterns("/**/*.css", "/**/*.js", "/**/*.png", "/**/*.jpg", "/**/*.jpeg")
                .addPathPatterns("/register", "/login");   // 攔截注冊(cè) 和登錄

    }

2、攔截器應(yīng)用

  • 在請(qǐng)求開始時(shí)查詢登錄用戶。
  • 在本次請(qǐng)求中持有用戶數(shù)據(jù)
  • 在模板視圖上顯示用戶數(shù)據(jù)
  • 在請(qǐng)求結(jié)束時(shí)清理用戶數(shù)據(jù)

攔截器——LoginTicketInterceptor

每次請(qǐng)求的過程
【論壇java項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、

request獲取Cookie——CookieUtil
  • 復(fù)用request獲取Cookie
  • 返回Cookie中的值
public class CookieUtil {

    public static String getValue(HttpServletRequest request, String name) {
        if (request == null || name == null) {
            throw new IllegalArgumentException("參數(shù)為空!");
        }

        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對(duì)象.
/**
 * 持有用戶信息,用于代替session對(duì)象.
 */
@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);
            // 檢查憑證是否有效
            // 憑證不為空、狀態(tài)為 0、超時(shí)時(shí)間晚于登陸時(shí)間
            if (loginTicket != null && loginTicket.getStatus() == 0 && loginTicket.getExpired().after(new Date())) {
                // 根據(jù)憑證查詢用戶
                User user = userService.findUserById(loginTicket.getUserId());
                // 在本次請(qǐng)求中持有用戶
                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 {
        // 清理數(shù)據(jù)
        hostHolder.clear();
    }
}

配置——WebMvcConfig
  @Autowired
    private LoginTicketInterceptor loginTicketInterceptor;

	  @Override
    public void addInterceptors(InterceptorRegistry registry) {
        /*
            攔截一切請(qǐng)求,不攔截的 加后邊
         */
        registry.addInterceptor(loginTicketInterceptor)
                .excludePathPatterns("/**/*.css", "/**/*.js", "/**/*.png", "/**/*.jpg", "/**/*.jpeg");
    }

首頁——index

登錄 才能看到 消息

【論壇java項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、

沒登錄 才顯示 注冊(cè)

【論壇java項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、

沒登錄 才顯示 登錄

【論壇java項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、

調(diào)整登錄賬號(hào)顯示

【論壇java項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、

【論壇java項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、

3、運(yùn)行結(jié)果:

【論壇java項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、

【論壇java項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、

【論壇java項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、

4、總結(jié)

關(guān)于Spring MVC攔截器:

  • 攔截器需實(shí)現(xiàn)HandlerInterceptor接口,而WebMvcConfigurer接口是MVC配置類要實(shí)現(xiàn)的接口
  • preHandle方法在Controller之前執(zhí)行,若返回false,則終止執(zhí)行后續(xù)的請(qǐng)求。
  • postHandle方法在Controller之后、模板之前執(zhí)行。
  • afterCompletion方法在模板之后執(zhí)行。

關(guān)于配置Spring MVC攔截器

  • 配置類需實(shí)現(xiàn)WebMvcConfigurer接口
  • 通過addInterceptors方法對(duì)攔截器進(jìn)行配置
  • 可以配置忽略攔截的路徑,也可以配置希望攔截的路徑

關(guān)于ThreadLocal的描述

  • ThreadLocal采用線程隔離的方式存放數(shù)據(jù),可以避免多線程之間出現(xiàn)數(shù)據(jù)訪問沖突。
  • ThreadLocal提供set方法,能夠以當(dāng)前線程為key存放數(shù)據(jù)。
  • ThreadLocal提供get方法,能夠以當(dāng)前線程為key獲取數(shù)據(jù)。

七、賬號(hào)設(shè)置——上傳頭像、修改密碼

【論壇java項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、
上傳文件

  • 請(qǐng)求:必須是POST請(qǐng)求
  • 表單:enctype="multipart/form-data'
  • Spring MVC:通過MultipartFile處理上傳文件

開發(fā)步驟

  • 訪問賬號(hào)設(shè)置頁面
  • 上傳頭像
  • 獲取頭像

完成這個(gè)頁面
【論壇java項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、

1、可以訪問這個(gè)頁面

返回訪問頁面——UserController

【論壇java項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、

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

顯示頁面——setting.html

修改路徑等
【論壇java項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、
【論壇java項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、
【論壇java項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、

index中修改——鏈接

【論壇java項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、

2、上傳頭像

  • 開放時(shí),是Windows
  • 上線時(shí),是 Linux

配置文件

添加 存儲(chǔ) 上傳文件的路徑

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

UserService

更新修改圖像的路徑,返回更新行數(shù)

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

UserController

  • 上傳表單提交為post
    請(qǐng)求
  • 項(xiàng)目域名
  • 項(xiàng)目名

	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;     //項(xiàng)目名

    @Autowired
    private UserService userService;

    @Autowired
    private HostHolder hostHolder;      //取 當(dāng)前用戶是誰
    
    @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";
        }

        // 生成隨機(jī)文件名
        fileName = CommunityUtil.generateUUID() + suffix;
        // 確定文件存放的路徑
        File dest = new File(uploadPath + "/" + fileName);
        try {
            // 存儲(chǔ)文件
            headerImage.transferTo(dest);
        } catch (IOException e) {
            logger.error("上傳文件失敗: " + e.getMessage());
            throw new RuntimeException("上傳文件失敗,服務(wù)器發(fā)生異常!", e);
        }

        // 更新當(dāng)前用戶的頭像的路徑(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) {
        // 服務(wù)器存放路徑
        fileName = uploadPath + "/" + fileName;
        // 文件后綴
        String suffix = fileName.substring(fileName.lastIndexOf("."));
        // 響應(yīng)圖片
        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());
        }
    }

賬號(hào)設(shè)置——setting.html

【論壇java項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、
【論壇java項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、

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

        // 驗(yàn)證郵箱
        User user = userMapper.selectByEmail(email);
        if (user == null) {
            map.put("emailMsg", "該郵箱尚未注冊(cè)!");
            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;
        }

        // 驗(yàn)證原始密碼
        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

更改

總結(jié)

上傳文件的必要條件

  • 必須在POST請(qǐng)求中上傳文件
  • 表單的enctype屬性必須設(shè)置為“multipart/form-data”

關(guān)于上傳路徑與訪問路徑的描述

  • 上傳路徑可以是本地路徑也可以是web路徑,
  • 訪問路徑必須是符合HTTP協(xié)議的Web路徑。

關(guān)于MultipartFile類型的描述

  • 一個(gè)MultipartFile只能封裝一個(gè)文件
  • 通過MultipartFilegetOriginalFilename方法,可以獲得原始文件名
  • 通過MultipartFiletransferTo方法,可以將文件存入指定位置

八、檢查登錄狀態(tài)

【論壇java項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、

  • 沒有登陸也能訪問登錄后的頁面
  • 安全隱患
  • 攔截器——不在配置文件中攔截,用注解在方法上攔截

使用攔截器

  • 在方法前標(biāo)注自定義注解
  • 攔截所有請(qǐng)求,只處理帶有該注解的方法

自定義注解

常用的元注解:

  • @Target、——聲明自定義注解作用在哪個(gè)位置,例如方法上、類上
  • @Retention、——聲明自定義注解的有效時(shí)間(編譯時(shí)、運(yùn)行時(shí))
  • @Document、——聲明自定義注解生成文檔的時(shí)候要不要把注解帶上去
  • @Inherited——用于繼承,父類有注解,子類是否要繼承

如何讀取注解:

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

1、寫注解——LoginRequired

新建一個(gè)包,寫注解
【論壇java項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、

【論壇java項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、

@Target(ElementType.METHOD)   // 方法上
@Retention(RetentionPolicy.RUNTIME)  //程序運(yùn)行時(shí)有效
public @interface LoginRequired {

        // 里邊不用寫內(nèi)容,標(biāo)注解就行
}

2、加上注解——UserController

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

【論壇java項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、

3、攔截器——LoginRequiredInterceptor

【論壇java項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、

@Component
public class LoginRequiredInterceptor implements HandlerInterceptor {

    @Autowired
    private HostHolder hostHolder;   // 獲取當(dāng)前用戶

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 判斷攔截到的是不是方法
        if (handler instanceof HandlerMethod) {
            HandlerMethod handlerMethod = (HandlerMethod) handler;  // 轉(zhuǎn)型
            Method method = handlerMethod.getMethod();  // 獲得方法
            LoginRequired loginRequired = method.getAnnotation(LoginRequired.class); // 從方法里取注解
            // 當(dāng)前方法需要登錄,但是用戶沒登錄
            if (loginRequired != null && hostHolder.getUser() == null) {
                // 重定向 —— 項(xiàng)目名
                response.sendRedirect(request.getContextPath() + "/login");
                return false;  // 拒絕請(qǐng)求
            }
        }
        return true;
    }
}

4、攔截器配置——WebMvcConfig

攔截器配置——指定生成的路徑

好處,攔截誰,就給誰加注解

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

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        /*
            攔截一切請(qǐng)求,不攔截的 加后邊
         */
        //  /**/*.css _ static 目錄下

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

    }

總結(jié)

關(guān)于元注解

  • @Target用于描述該注解可以作用的目標(biāo)類型
  • @Retention用于描述該注解被保留的時(shí)間
  • @Document用于描述該注解是否可以生成到文檔里
  • 比如LoginRequired加上了這個(gè)@Inherited,那注解LoginRequired的類的子類也會(huì)自動(dòng)注解上LoginRequired

關(guān)于解析注解

  • 在程序中,可以通過反射的方式解析注解
  • 通過Method對(duì)象可以獲取某方法上標(biāo)注的所有注解
  • 通過Method對(duì)象可以獲取某方法上指定類型的注解
  • Method對(duì)象上還有很多其他的方法,可以獲取該方法上標(biāo)注的注解

在程序中,可以通過哪些方式正確實(shí)現(xiàn)重定向文章來源地址http://www.zghlxwxcb.cn/news/detail-462018.html

  • 在Controller的方法里,通過返回以”redirect”開頭的字符串實(shí)現(xiàn)重定向
  • 在Controller的方法里,通過response對(duì)象的sendRedirect方法實(shí)現(xiàn)重定向
  • 在攔截器中,通過response對(duì)象的sendRedirect方法實(shí)現(xiàn)重定向

到了這里,關(guān)于【論壇java項(xiàng)目】第二章 Spring Boot實(shí)踐,開發(fā)社區(qū)登錄模塊:發(fā)送郵件、開發(fā)注冊(cè)功能、會(huì)話管理、生成驗(yàn)證碼、開發(fā)登錄、退出功能、的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • (SpringBoot)第二章:Spring創(chuàng)建和使用

    注意 :在Java中對(duì)象也叫做Bean,所以后續(xù)文章中用Be

    2024年02月08日
    瀏覽(28)
  • 頭歌實(shí)踐教學(xué)平臺(tái)Python-Python第二章作業(yè)(初級(jí))

    頭歌實(shí)踐教學(xué)平臺(tái)Python-Python第二章作業(yè)(初級(jí))

    第1關(guān):三角形周長(zhǎng)及面積 任務(wù)描述 輸入的三角形的三條邊a、b、c 的長(zhǎng)度,計(jì)算并依次輸出三角形的周長(zhǎng)和面積,結(jié)果嚴(yán)格保留2位小數(shù)。測(cè)試用例的數(shù)據(jù)保證三角形三邊數(shù)據(jù)可以構(gòu)成三角形。 三角形面積計(jì)算公式: ,其中s=(a+b+c)/2。 ?第2關(guān):三角函數(shù)計(jì)算 根據(jù)下面公式 計(jì)

    2024年02月08日
    瀏覽(95)
  • k8s學(xué)習(xí) — (實(shí)踐)第二章 搭建k8s集群

    k8s學(xué)習(xí) — (實(shí)踐)第二章 搭建k8s集群

    k8s學(xué)習(xí) — 各章節(jié)重要知識(shí)點(diǎn) 推薦學(xué)習(xí)時(shí)使用,輕量化的k8s集群,可以在個(gè)人電腦上使用。 minikube 是一個(gè)工具, 能讓你在本地運(yùn)行 Kubernetes。 minikube 在你的個(gè)人計(jì)算機(jī)(包括 Windows、macOS 和 Linux PC)上運(yùn)行一個(gè)一體化(all-in-one)或多節(jié)點(diǎn)的本地 Kubernetes 集群,以便你來嘗試

    2024年02月03日
    瀏覽(30)
  • [第二章—Spring MVC的高級(jí)技術(shù)]  2.3 處理異常

    [第二章—Spring MVC的高級(jí)技術(shù)] 2.3 處理異常

    各位小猿,程序員小猿開發(fā)筆記,希望大家共同進(jìn)步。 引言 我是誰——異常處理。 來自那——所有功能正常運(yùn)行,但出現(xiàn)錯(cuò)誤 怎么辦——如何處理異常和響應(yīng)客戶端 我是誰——Spring框架中的一個(gè)注解 用在哪——應(yīng)用在控制器類或方法上 什么用——用于在控制器方法中指定

    2024年01月22日
    瀏覽(31)
  • 【Java入門合集】第二章Java語言基礎(chǔ)(三)

    【Java入門合集】第二章Java語言基礎(chǔ)(三)

    博主:命運(yùn)之光 專欄:Java零基礎(chǔ)入門 學(xué)習(xí)目標(biāo) 掌握變量、常量、表達(dá)式的概念,數(shù)據(jù)類型及變量的定義方法; 掌握常用運(yùn)算符的使用; 掌握程序的順序結(jié)構(gòu)、選擇結(jié)構(gòu)和循環(huán)結(jié)構(gòu)的使用; 掌握數(shù)組的定義及使用方法; 掌握基本的輸入輸出方法; Java中的語句有很多種形式

    2024年02月03日
    瀏覽(91)
  • 【Java入門合集】第二章Java語言基礎(chǔ)(一)

    【Java入門合集】第二章Java語言基礎(chǔ)(一)

    博主:命運(yùn)之光 專欄:Java零基礎(chǔ)入門 學(xué)習(xí)目標(biāo) 掌握變量、常量、表達(dá)式的概念,數(shù)據(jù)類型及變量的定義方法; 掌握常用運(yùn)算符的使用; 掌握程序的順序結(jié)構(gòu)、選擇結(jié)構(gòu)和循環(huán)結(jié)構(gòu)的使用; 掌握數(shù)組的定義及使用方法; 掌握基本的輸入輸出方法; 提示:不要去強(qiáng)記

    2024年02月02日
    瀏覽(33)
  • 【軟考高級(jí)信息系統(tǒng)項(xiàng)目管理師--第二章:信息技術(shù)發(fā)展】

    【軟考高級(jí)信息系統(tǒng)項(xiàng)目管理師--第二章:信息技術(shù)發(fā)展】

    ?? 作者 :“碼上有前” ?? 文章簡(jiǎn)介 :軟考高級(jí)–信息系統(tǒng)項(xiàng)目管理師 ?? 歡迎小伙伴們 點(diǎn)贊??、收藏?、留言?? 網(wǎng)絡(luò)標(biāo)準(zhǔn)協(xié)議 OSI七層 物理層(RS232、V.35、RJ-45、FDDI) 數(shù)據(jù)鏈路層(IEEE802.3/.2、HDLC、PPP、ATM) 網(wǎng)絡(luò)層(IP、ICMP、IGMP、IPX、ARP【IP】】) 傳輸層(TCP、UDP、SPX) 會(huì)話層

    2024年02月21日
    瀏覽(91)
  • 第二章--Java多線程高并發(fā)+面試題

    第二章--Java多線程高并發(fā)+面試題

    s1 基礎(chǔ)知識(shí) 001 并發(fā)編程的優(yōu)缺點(diǎn) 優(yōu)點(diǎn) 充分利用多核cpu的計(jì)算能力 方便業(yè)務(wù)拆分,提升系統(tǒng)并發(fā)能力和性能,提升程序執(zhí)行效率,提速 缺點(diǎn) 內(nèi)存泄漏 | 上下文切換 線程安全 | 死鎖 002 并發(fā)編程三要素 三要素 安全問題的原因 具體原因 對(duì)策 原子性 線程切換 一個(gè)或多個(gè)操作要么

    2024年02月06日
    瀏覽(32)
  • 第二章:項(xiàng)目環(huán)境搭建【基于Servlet+JSP的圖書管理系統(tǒng)】

    第二章:項(xiàng)目環(huán)境搭建【基于Servlet+JSP的圖書管理系統(tǒng)】

    02-圖書管理系統(tǒng)-項(xiàng)目環(huán)境搭建 ??本項(xiàng)目涉及到的工具都有在云盤提供,自行下載即可 JDK8 IDEA2021 Tomcat8.5 MySQL的客戶端工具SQLYog … ??通過IDEA創(chuàng)建maven項(xiàng)目。勾選腳手架工具。選擇 maven-archetype-webapp 設(shè)置項(xiàng)目的基礎(chǔ)信息 3.1 JDK配置 ??JDK使用的是JDK8。我們也需要配置下:

    2024年02月11日
    瀏覽(25)
  • 第二章 02Java基礎(chǔ)-數(shù)據(jù)類型、標(biāo)識(shí)符、鍵盤錄入

    今天我們學(xué)習(xí)Java基礎(chǔ),數(shù)據(jù)類型、標(biāo)識(shí)符、鍵盤錄入 1.數(shù)據(jù)類型大體上可以分為兩類,一類是基本數(shù)據(jù)類型,另外一類是引用數(shù)據(jù)類型。今天我們學(xué)習(xí)基本數(shù)據(jù)類型。 2.基本數(shù)據(jù)類型可以分為四類八種,整數(shù)(byte short int long)、浮點(diǎn)數(shù)(float double)、字符(char)和布爾(

    2024年02月06日
    瀏覽(96)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包