2.5 显示登录信息 ,修改个人设置 使用自定义注解检查登录状态



显示登录信息

在这里插入图片描述

拦截器示例

  • 定义拦截器实现HandlerInterceptor 实现其三个方法
  • preHandle // 在Controller之前执行 return false表示取消本次请求
  • postHandle // 在Controller之后执行,TemplateEngine之前执行执行
  • afterCompletion // 在TemplateEngine之后执行
@Component
public class AlphaInterceptor implements HandlerInterceptor {
    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());
        return true;
    }

    // 在Controller之后执行,TemplateEngine之前执行执行
    @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());
    }
}

配置拦截器,指定拦截路径和排除的路径

  • addInterceptor(alphaInterceptor) 添加要配置的拦截器
  • excludePathPatterns 排除不要拦截的路径 多是静态资源
  • addPathPatterns 添加需要拦截的路径
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Autowired
    private AlphaInterceptor alphaInterceptor;
    
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(alphaInterceptor)
                .excludePathPatterns("/**/*.css", "/**/*.js", "/**/*.png", "/**/*.jpg", "/**/*.jpeg")
                .addPathPatterns("/register", "/login");
        
    }

}

object handler 是拦截的目标

在这里插入图片描述

拦截器应用

登录的一次请求过程

在这里插入图片描述

1、编写一个工具类从请求中来获取cookie的值

public class CookieUtil {

    public static String getValue(HttpServletRequest request, String name) {
        if (request == null || name == null) {
            throw new IllegalArgumentException("参数为空!");
        }
        //遍历得到的cookie集合找到值相等的cookie,获取其value
        Cookie[] cookies = request.getCookies();
        if (cookies != null) {
            for (Cookie cookie : cookies) {
                if (cookie.getName().equals(name)) {
                    return cookie.getValue();
                }
            }
        }
        return null;
    }

}

2、编写工具来持有用户信息

  • 以线程为key存取值
  • ThreadLocal可以把用户信息保存在线程中,用户发来的每一次请求启动的线程会保存用户的信息,当请求结束,保存的用户信息会被清除掉,方便我们在开发中获取用户登录信息,从而不需要每次取登录信息都需要从HttpServletRequest中取。
@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();
    }

}

3、编写LoginTicketInterceptor过滤器

  • 请求之初,controller之前 保存用户登录信息
  • 模板引擎调用之前 获取保存的用户信息
  • 模板引擎调用之后 最后清除用户登录信息
@Component
public class LoginTicketInterceptor implements HandlerInterceptor {
    private static final Logger logger = LoggerFactory.getLogger(AlphaInterceptor.class);

    @Autowired(required = false)
    private UserService userService;

    @Autowired
    private HostHolder hostHolder;


    //请求之初,controller之前
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //从cookie中获取ticket
        String ticket = CookieUtil.getValue(request, "ticket");
        if(ticket!=null)
        {
            //检查其状态是否有效
            LoginTicket loginTicket = userService.findLoginTicket(ticket);
            if (loginTicket != null && loginTicket.getStatus() == 0 && loginTicket.getExpired().after(new Date())) {
                // 根据凭证查询用户
                User user = userService.queryUserById(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) {
            modelAndView.addObject("loginUser", user);
        }
    }

    //模板引擎调用之后 最后清理数据
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        hostHolder.clear();
    }
}

4、配置拦截器,指定拦截路径和排除的路径

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Autowired
    private AlphaInterceptor alphaInterceptor;
    
    @Autowired
    private LoginTicketInterceptor loginTicketInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(alphaInterceptor)
                .excludePathPatterns("/**/*.css", "/**/*.js", "/**/*.png", "/**/*.jpg", "/**/*.jpeg")
                .addPathPatterns("/register", "/login");

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

    }

}

5、修改页面

当用户登录之前显示注册登录,登录之后显示个人信息

账号设置

在这里插入图片描述

1、编写UserController处理设置请求

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

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

2、处理上传头像请求

保存图片路径暂时设置为本地服务器路径 为了方便更改,在properties中进行配置

community.path.upload=D:/work/upload

spring mvc MultipartFile是表现层对象 如果放到service处理上传业务会有耦合 因此放到controller处理上传操作

业务层只处理更新头像路径

//更新头像路径
public int updateHeader(int id,String header_url)
{
    return userMapper.UpdateHeader(id,header_url);
}

如果上传多个文件就声明成一个数组

取文件后缀名

@RequestMapping(value = "/upload",method = RequestMethod.POST)
public String uploadHeader(MultipartFile headerImage, Model model)
{
    if(headerImage==null)
    {
        model.addAttribute("headImageMsg","您还没有选择想要上传的图片!");
        return "/site/setting";
    }
    //获取用户上传图片的后缀名
    String fileName=headerImage.getOriginalFilename();
    String suffix=fileName.substring(fileName.lastIndexOf("."));
    if(StringUtils.isBlank(suffix))
    {
        model.addAttribute("headImageMsg","请选择正确的图片格式!");
        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 url=domain+contextPath+"/user/header/"+fileName;
    userService.updateHeader(user.getId(),url);
    return "redirect:/index";
    
}

3、处理获取头像请求

java7的新语法

可以在try后面加()相当于finally

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


}
<!-- 上传头像 -->
<h6 class="text-left text-info border-bottom pb-2">上传头像</h6>
<form class="mt-5" method="post" enctype="multipart/form-data" th:action="@{/user/upload}">
   <div class="form-group row mt-4">
      <label for="head-image" class="col-sm-2 col-form-label text-right">选择头像:</label>
      <div class="col-sm-10">
         <div class="custom-file">
            <input type="file" th:class="|custom-file-input ${headImageMsg!=null?'is-invalid':''}|"
                  id="head-image" name="headerImage" lang="es" required="">
            <label class="custom-file-label" for="head-image" data-browse="文件">选择一张图片</label>
            <div class="invalid-feedback" th:text="${headImageMsg}">
               上传图片错误!
            </div>
         </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>

开发修改密码功能:

1、在账号设置页面,填写原密码以及新密码,点击保存时将数据提交给服务器。

2、服务器检查原密码是否正确,若正确则将密码修改为新密码,并重定向到退出功能,强制用户重新登录。若错误则返回到账号设置页面,给与相应提示。

 //需要向页面发送信息需要Model   参数要和输入框的name一致
    @LoginRequired
    @RequestMapping(value = "/updatePassword",method = RequestMethod.GET)
    public String updatePassword(Model model,String oldPassword,String newPassword)
    {
        //@LoginRequired注解已经做了判断用户是否登录的功能 因此不需要在判断一次
        //服务器检查原密码是否正确,若正确则将密码修改为新密码,
        //先获取用户登录的user
        User user = hostHolder.getUser();
        oldPassword=CommunityUtil.MD5(oldPassword+user.getSalt());
        if(oldPassword.equals(user.getPassword()))
        {
            //生成新的加密密码并且保存
            newPassword=CommunityUtil.MD5(newPassword+user.getSalt());
            userService.updatePassword(user.getId(),newPassword);
            //并重定向到退出功能,强制用户重新登录。
            //清楚线程持有的用户数据
            hostHolder.clear();
            return "redirect:/login";
        }else {
            model.addAttribute("passwordMsg","原密码错误,清重新输入!");
            return "/site/setting";
        }

    }

使用自定义注解检查登录状态

在方法上加注解拦截

@Target 自定义注解作用域

@Retention 生效时间 编译还是运行

@Document 是否生成文档

@Inherited 如果某个类使用@Inherited修饰,则该类的子类将自动使用@Inherited修饰。 比如LoginRequired加上了这个@Inherited,那注解LoginRequired的类的子类也会自动注解上LoginRequired

1、创建自定义注解

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

2、在需要拦截的方法上加上自定义的注解

3、定义拦截器

先判断拦截的是不是方法

@Component
public class LoginRequiredInterceptor implements HandlerInterceptor {

    @Autowired
    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);
            //如果方法有注解并且登录信息为空就返回false取消请求
            //并且重定向到login
            if(loginRequired!=null&&hostHolder.getUser()==null)
            {
                response.sendRedirect(request.getContextPath()+"/login");
                return  false;
            }
        }
        return true;
    }

   
}

4、配置拦截器

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Autowired
    private AlphaInterceptor alphaInterceptor;

    @Autowired
    private LoginTicketInterceptor loginTicketInterceptor;
    
    @Autowired
    private LoginRequiredInterceptor loginRequiredInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(alphaInterceptor)
                .excludePathPatterns("/**/*.css", "/**/*.js", "/**/*.png", "/**/*.jpg", "/**/*.jpeg")
                .addPathPatterns("/register", "/login");

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

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

}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值