4.账号设置 + 修改密码 + 检查登陆状态

目录

1.账号设置

1.1 访问账号设置页面

1.2 修改模板

1.3 上传文件

1.4 获取头像

1.5 处理页面表单

2.修改密码

UserController:

UserService:

setting.html

3.检查登录状态

3.1 拦截器拦截带有注解 @LoginRequired 的方法,判断是否登录

3.2 配置拦截器


1.账号设置

  • 上传文件:请求(必须是 POST 请求)、表单(enctype = "multipart/form-data")、Spring MVC(通过 MultipartFile 处理上传文件)
  • 开发步骤:访问账号设置、上传头像、获取头像

1.1 访问账号设置页面

需要开发 controller 声明模板存放路径,在 controller 包下新建 Usertroller 类(用户相关业务):

  • 添加方法使得浏览器通过方法访问到设置的页面
package com.example.demo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
@RequestMapping("/user")
public class UserController {

    @RequestMapping(path = "/setting", method = RequestMethod.GET)
    //添加方法使得浏览器通过方法访问到设置的页面
    public String getSettingPage() {
        return "/site/setting";
    }
}

1.2 修改模板

配置 setting 模板:

修改 index.xml:

1.3 上传文件

需要配置上传资源的位置,在 resources 资源文件下的 application.properties 中添加配置:

#上传文件配置位置
community.path.upload=e:/forum-system/data/upload

上传文件请求按照服务器三重架构,从下往上开发:数据访问层(上传文件存储到某一个位置,不是数据库中,因此数据访问层没有修改的)、业务层(上传文件然后需要更新用户的 headurl)、上传文件在 controller 中实现(MultipartFile 处理文件,属于表现层的对象)

在 service 包下的 Userservice 类中添加:

  • 添加方法:更新修改头像路径,返回更新行数(int)
//更新修改头像路径
public int updateHeader(int userId, String headerUrl) {
    return userMapper.updateHeader(userId, headerUrl);
}

在 controller 类下的 UserController 类中添加:

  • 追加请求:处理本次上传文件请求
  • 注入上传路径、域名、项目的访问路径
  • 注入 UserService
  • 注入 HostHeader:更新当前用户头像
  • 加入方法:处理上传逻辑,是一个 POST 请求
  • 使用 MultipartFile 接收一个文件
  • 如果没有上传文件,给予提示并且返回当前页面
  • 给文件生成随机名字(不可以使用原文件名,避免重复),后缀不可以变
  • 首先得到原始文件名,然后截取后缀(截取最后一个“ . ”的索引)
  • 再去判断截取是否为空:为空给予提示并且返回当前页面
  • 然后生成随机文件
  • 确定文件存放路径
  • 将当前文件中的内容写到目标文件中
  • 更新当前用户的头像路径(web 访问路径):http://localhost:8080/community/user/header/xxx.png
  • 1.得到当前用户   2.得到 headerUrl:域名 + 项目名 + 当前 controller 访问路径 + 文件名(随机生成的文件名)
  • 调用 Userservice,传入当前用户和更新后的图像路径,重定向到首页
package com.example.demo.controller;
import com.example.demo.entity.User;
import com.example.demo.service.UserService;

import com.example.demo.util.CommunityUtil;
import com.example.demo.util.HostHolder;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;


@Controller
@RequestMapping("/user")
public class UserController {

    //打印日志
    private static final Logger logger = LoggerFactory.getLogger(UserController.class);

    //注入 UserService
    @Autowired
    private UserService userService;

    //注入 HostHeader:更新当前用户头像
    @Autowired
    private HostHolder hostHolder;

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

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

    //注入项目访问路径
    @Value("${server.servlet.context-path}")
    private String contextPath;

    //加入方法:处理上传逻辑,是一个 POST 请求
    @RequestMapping(path = "/upload", method = RequestMethod.POST)
    //使用 MultipartFile 接收一个文件
    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("上传文件失败,服务器发生异常");
        }

        //更新当前用户的头像路径(web 访问路径):http://localhost:8080/community/user/header/xxx.png
        //1.得到当前用户 2.得到 headerUrl:域名 + 项目名 + 当前 controller 访问路径 + 文件名(随机生成的文件名)
        User user = hostHolder.getUser();
        String headerUrl = domain + contextPath + "/user/header" + fileName;

        //调用 Userservice,传入当前用户和更新后的图像路径,重定向到首页
        userService.updateHeader(user.getId(), headerUrl);
        return "redirect:/index";
    }
}

1.4 获取头像

在 controller 类下的 UserController 类中添加:

  • 添加获取头像访问路径:header/{fileName}(http://localhost:8080/community/user/header/xxx.png),是一个 GET 请求,只取头像路径
  • 添加获取头像方法:返回 void,是一个图片,二进制数据,需要通过流手动向浏览器输出
  • 添加注解 @PathVariable:解析 fileName 参数;向浏览器响应特殊东西,需要用response 去拼
  • 找到服务器存放的路径,然后带上本地路径的文件
  • 向浏览器输出图片:首先是格式(解析后缀),接下来是响应图片(二进制,使用字节流),获取字节流得到输出流,输出文件(创建文件得到输入流去边读边输出)
  • 建立缓冲区,一次输出 1024 字节
  • spring mvc 会自动的关闭输出流,输入流是自己创建的需要手动关闭
    //添加获取头像访问路径:header/{fileName}(http://localhost:8080/community/user/header/xxx.png),是一个 GET 请求,只取头像路径
    @RequestMapping(path = "/header/{fileName}", method = RequestMethod.GET)
    //添加注解 @PathVariable:解析 fileName 参数;向浏览器响应特殊东西,需要用response 去拼
    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());
        }

    }

1.5 处理页面表单

2.修改密码

当用户在账号设置页面修改密码时,输入完原始密码以及新密码、确认密码后,当点击【立即保存】后,若用户原始密码输入正确,新密码与确认密码一致时,会跳转至登录页面重新登录,若输入有误,则显示对应的错误信息

UserController:

//修改密码
    @RequestMapping(value = "/updatePassword",method = RequestMethod.POST)
    //@CookieValue("ticket") String ticket:从浏览器器中得到cookie
    public String updatePassword(String oldPassword, String newPassword, String confirmNewPassword, 
                                 Model model,@CookieValue("ticket") String ticket){
        //对传入参数进行判断,不能为空
        if(StringUtils.isBlank(oldPassword)){
            model.addAttribute("oldPasswordMsg","请输入原始密码!");
            return "site/setting";
        }
        if(StringUtils.isBlank(newPassword)){
            model.addAttribute("newPasswordMsg","请输入新密码!");
            return "site/setting";
        }
        if(StringUtils.isBlank(confirmNewPassword)){
            model.addAttribute("confirmNewPasswordMsg","请输入确认密码!");
            return "site/setting";
        }
        //首先通过所持有的的用户对象获取当前用户
        User user = hostHolder.getUser();
        //判断用户输入的原密码是否与存储的原密码一致
        //首先对用户输入的原密码进行加密处理
         oldPassword = CommunityUtil.md5(oldPassword+user.getSalt());
         if(!oldPassword.equals(user.getPassword())){
             model.addAttribute("oldPasswordMsg","该密码与原密码不符!");
             return "site/setting";
         }
         //判断新输入密码与原密码是否一致
        //对新密码进行加密
        newPassword=CommunityUtil.md5(newPassword+user.getSalt());
        if(newPassword.equals(user.getPassword())){//判断
            model.addAttribute("newPasswordMsg","新密码与原密码一致!");
            return "site/setting";
        }
        //对确认密码进行加密
        confirmNewPassword=CommunityUtil.md5(confirmNewPassword+user.getSalt());
        if(!newPassword.equals(confirmNewPassword)){//判断
            model.addAttribute("confirmNewPasswordMsg","两次密码不一致!");
            return "site/setting";
        }
        userService.updatePassword(user.getId(),newPassword);
        //修改密码后,用户需要重新登陆,所以在本次持有中释放用户
        userService.logout(ticket);
        return "redirect:/login";
    }

UserService:

通过当前的用户id,和传入的密码,修改用户的密码

//修改密码
public int updatePassword(int userId,String password){
    return userMapper.updatePassword(userId,password);
}

setting.html

<!-- 修改密码 -->
				<h6 class="text-left text-info border-bottom pb-2 mt-5">修改密码</h6>
				<form class="mt-5" method="post" th:action="@{/user/updatePassword}">
					<div class="form-group row mt-4">
						<label for="old-password" class="col-sm-2 col-form-label text-right">原密码:</label>
						<div class="col-sm-10">
							<input type="password" th:class="|form-control ${oldPasswordMsg!=null ? 'is-invalid':'' }|"
								   name="oldPassword"
								   id="old-password" placeholder="请输入原始密码!" required>
							<div class="invalid-feedback" th:text="${oldPasswordMsg}">
								密码长度不能小于8位!
							</div>							
						</div>
					</div>
					<div class="form-group row mt-4">
						<label for="new-password" class="col-sm-2 col-form-label text-right">新密码:</label>
						<div class="col-sm-10">
							<input type="password" th:class="|form-control ${newPasswordMsg!=null ? 'is-invalid':'' }|"
								   name="newPassword"
								   id="new-password" placeholder="请输入新的密码!" required>
							<div class="invalid-feedback" th:text="${newPasswordMsg}">
								密码长度不能小于8位!
							</div>							
						</div>
					</div>
					<div class="form-group row mt-4">
						<label for="confirm-password" class="col-sm-2 col-form-label text-right">确认密码:</label>
						<div class="col-sm-10">
							<input type="password" th:class="|form-control ${confirmNewPasswordMsg!=null ? 'is-invalid':'' }|"
								   name="confirmNewPassword"
								   id="confirm-password" placeholder="再次输入新密码!" required>
							<div class="invalid-feedback" th:text="${confirmNewPasswordMsg}">
								两次输入的密码不一致!
							</div>								
						</div>
					</div>				
					<div class="form-group row mt-4">
						<div class="col-sm-2"></div>
						<div class="col-sm-10 text-center">
							<button type="submit" class="btn btn-info text-white form-control">立即保存</button>
						</div>
					</div>
				</form>				

3.检查登录状态

没有登陆之前:访问 localhost:8080/community/index :看不到个人设置相关内容

但是访问 localhost:8080/community/user/setting :

上述就是一个漏洞,这是不安全的,需要让用户访问不到


  • 使用拦截器:在方法前标注自定义注解、拦截所有请求,只处理带有该注解的方法
  • 自定义注解:常见元注解(@Target、@Retention、@Document、@Inherited)、如何让读取注解(Method.getDeclearAnnotations()、Method.getAnnotation(Class<T> annotationClass))

自定义注解先创建一个 annotation 包,创建一个 LoginRequired 类(注解类):

  • 使用 @Target,描述注解使用在方法上
  • 使用 @Retention 声明注解有效时机
package com.example.demo.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LoginRequired {

}

哪些请求是登录以后才能访问:setting、upload

在 Usertroller 类中添加注解:

@LoginRequired
@RequestMapping(path = "/setting", method = RequestMethod.GET)

@LoginRequired
@RequestMapping(path = "/upload", method = RequestMethod.POST)

3.1 拦截器拦截带有注解 @LoginRequired 的方法,判断是否登录

在 controller 包下的 interceptor 包中新建 LoginRequiredInterceptor 类(拦截器):

  • 添加注解 @Component,实现接口 HandlerInterceptor
  • 在请求最初,判断是否登录:重写 preHandle 方法
  • 判断是否登录:尝试获取当前用户,注入 HostHolder
  • 判断拦截目标是否为方法,是方法接着处理
  • 获取拦截到的对象
  • 从获取到的对象取注解
  • 判断注解是否为空
  • 注解不为空,但是用户没有登录,拒绝访问,重定向到 login(登录页面) 路径(应用路径 + login 路径)
package com.example.demo.controller.interceptor;

import com.example.demo.annotation.LoginRequired;
import com.example.demo.util.HostHolder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;

//添加注解 @Component,实现接口 HandlerInterceptor
@Component
public class LoginRequiredInterceptor implements HandlerInterceptor {

    @Autowired
    private HostHolder hostHolder;

    //在请求最初,判断是否登录:重写 preHandle 方法
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
                             Object handler) throws Exception {
        //判断拦截目标是否为方法,是方法接着处理
        if (handler instanceof HandlerMethod) {
            判断是否登录:尝试获取当前用户,注入 HostHolder
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            //获取拦截到的对象
            Method method  =handlerMethod.getMethod();
            //从获取到的对象取注解
            LoginRequired loginRequired = method.getAnnotation(LoginRequired.class);
            //判断注解是否为空,注解不为空,但是用户没有登录,拒绝访问,重定向到 login 路径(应用路径 + login 路径)
            if (loginRequired != null && hostHolder.getUser() == null) {
                response.sendRedirect(request.getContextPath() + "/login");
                return false;
            }
        }
        return true;
    }
}

3.2 配置拦截器

在 config 包下的 WebMvcConfig 拦截器配置类中拦截静态资源:

  • 注入拦截器 LoginRequireInterceptor
  • 拦截所有静态资源
//登录状态拦截器
@Autowired
private LoginRequiredInterceptor loginRequiredInterceptor;

public void addInterceptors(InterceptorRegistry registry) {

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

处理所有动态资源,人为筛选带有注解的一部分


访问  localhost:8080/community/user/setting 直接跳转到登陆页面:

  • 17
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
使用c#2017窗体写一个学生基本信息注册基本控件如下1. private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e):当用户点击“用户协议”链接时,触发这个事件处理程序。 2. form1_Load(object sender, EventArgs e):窗体加载时调用这个方法。 3. comboBox1.SelectedIndex = 0;:将下拉框选中第一个选项。 4. button2.Enabled = checkBox1.Checked;:根据复选框状态启用或禁用按钮2。 5. info += "账号:" + textBox1.Text.Trim() + "\n";:将用户名拼接到消息框中。 6. info += "密码:" + textBox4.Text.Trim() + "\n";:将密码拼接到消息框中。 7. info += "性别:" + this.GetSexInput() + "\n";:将性别拼接到消息框中。 8. info += "生日:" + dateTimePicker1.Value.ToShortDateString() + "\n";:将生日拼接到消息框中。 9. info += "QQ:" + textBox1.Text.Trim() + "\n";:将QQ号拼接到消息框中。 10. info += "喜爱的编程:" + GetFaavorLangInput() + "\n";:将喜欢的编程语言拼接到消息框中。 11. info += "爱好:" + GetHobbiesInput() + "\n";:将爱好拼接到消息框中。 12. MessageBox.Show(info, "确认你的注册信息!");:弹出一个消息框显示所有输入的信息。 13. bool ValidateInput():定义了一个名为ValidateInput的方法用于验证输入的用户名、密码、性别、生日、QQ和喜欢的编程语言等信息是否正确。 14. string GetSexInput():定义了一个名为GetSexInput的方法用于获取用户的性别。 15. string GetFaavorLangInput():定义了一个名为GetFaavorLangInput的方法用于获取用户的喜欢的编程语言。 16. string GetHobbiesInput():定义了一个名为GetHobbiesInput的方法用于获取用户的爱好
06-12
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

奋斗小温

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值