栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

SpringBoot从零开始的问答社区 (5)注册登录-拦截器

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

SpringBoot从零开始的问答社区 (5)注册登录-拦截器

代码仓库

Github->GitHub - noahlin27/Astera: Astera project for Spring Boot

Gitee->Astera: Astera project for Spring Boot

注册登录功能

在 service 包创建 RegloginService,接着创建它的实现类,然后在 controller 包创建 RegloginController。尽量在 service 实现逻辑功能,而 controller 处理外部请求时只负责调用 service,以及封装数据并把数据返回给前端。

注册

要对用户注册时设置的用户名、密码等做一些规定,比如用户名的合法性:允许使用的字符、允许的长度、不允许重复、不允许出现敏感词;密码的安全性:长度要求、字符要求等。在保存密码时,使用加密后的密文保存,以防万一。为了防止批量注册,可以增加邮箱/手机验证。

这里我只简单地对用户名做了非空和去重判断,密码做了非空判断,而密码的保存用“加盐”的方式保证数据的安全性。

生成默认头像,可以在静态资源文件中预先添加若干图片,在用户注册的时候将 headUrl 的值设置为随机的图片 url 即可。

@Override
public Map register(String username, String password) {
    Map map = new HashMap<>();
    if (StringUtils.isBlank(username)) {
        map.put("msg", "用户名不能为空");
        return map;
    }
    if (StringUtils.isBlank(password)) {
        map.put("msg", "密码不能为空");
        return map;
    }

    if (userDAO.selectByName(username) != null) {
        map.put("msg", "用户名已存在");
        return map;
    }

    User user = new User();
    Random random = new Random();
    user.setName(username);
    user.setSalt(UUID.randomUUID().toString().substring(0, 5));
    user.setHeadUrl(String.format("/images/img/%d.png", random.nextInt(36)));
    user.setPassword(MD5Util.Encode(password + user.getSalt(), "utf-8"));
    userDAO.insert(user);
    map.put("success", "注册成功! 请登录");
    return map;
}
登录

如果用户登录时输入的用户名或密码不正确,可以适当地给用户反馈一些提示信息;在密码校验成功后,发放 token 令牌给用户,并进行登记,token 可以有多种实现方式,而 cookie 是在浏览器客户端被广泛使用的一种。从浏览器请求中获取 cookie 会用到 HttpServletRequest 中的 getcookies() 方法,而添加 cookie 用到的是 HttpServletResponse 中的 addcookies() 方法。

服务端将令牌存储在数据库中,与用户 id 相关联。在数据库创建 login_ticket表,model 包创建 LoginTicket类,完成对应的 LoginTicketDAO,之后完成登录的业务逻辑。

@Override
public Map login(String username, String password) {
    Map map = new HashMap<>();
    User user = userDAO.selectByName(username);
    if (StringUtils.isBlank(username)) {
        map.put("msg", "用户名不能为空");
    }
    if (StringUtils.isBlank(password)) {
        map.put("msg", "密码不能为空");
    }

    if (user == null) {
        map.put("msg", "用户名不存在");
        return map;
    }

    if (!MD5Util.Encode(password + user.getSalt(), "utf-8").equals(user.getPassword())) {
        map.put("msg", "用户名或密码错误");
        return map;
    }

    String ticket = addTicket((user.getId()));
    map.put("ticket", ticket);
    return map;
}

private String addTicket(int userId) {
    LoginTicket loginTicket = new LoginTicket();
    loginTicket.setUserId(userId);
    Date now = new Date();
    now.setTime(now.getTime()+3600*1000*24);
    loginTicket.setExpired(now);
    loginTicket.setStatus(0);
    loginTicket.setTicket(UUID.randomUUID().toString().replaceAll("-",""));
    loginTicketDAO.insert(loginTicket);
    return loginTicket.getTicket();
}

@PostMapping("/login")
public String login(HttpServletRequest request, HttpServletResponse response,
                    @RequestParam("username") String username,
                    @RequestParam("password") String password,
                    @RequestParam("next") String next) {
    try {
        Map map = loginService.login(username, password);
        if (map.containsKey("ticket")) {
            cookie cookie = new cookie("ticket", map.get("ticket"));
            cookie.setPath("/");
            response.addcookie(cookie);

            if (StringUtils.isNotBlank(next)) {
                return "redirect:" + next;
            }
            return "redirect:/";
        } else {
            request.setAttribute("msg", map.get("msg"));
            return "login";
        }
    } catch (Exception e) {
        System.out.println("登录异常" + e.getMessage());
        return "login";
    }
}
登录状态验证 - 拦截器

当用户密码验证通过后,服务端给用户发放一个 token 令牌,保存在浏览器的 cookie 中,通常这个令牌只有一段时间的有效期。当客户端通过浏览器访问时,服务端就可以通过验证令牌来确认客户端的身份,只有在令牌存在且处于有效状态才可以通过验证,用拦截器 interceptor 可以实现 token 的校验。

新建 interceptor 包,创建 PassportInterceptor 类,继承 HandlerInterceptor,重写 preHandle(), postHandle(), afterCompletion() 方法。

@Component
public class PassportInterceptor implements HandlerInterceptor {
    @Resource
    LoginTicketDAO loginTicketDAO;

    @Resource
    UserDAO userDAO;

    @Resource
    HostHolder hostHolder;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String ticket = null;
        if (request.getcookies() != null) {
            for (cookie cookie : request.getcookies()){
                if (cookie.getName().equals("ticket")){
                    ticket = cookie.getValue();
                    break;
                }
            }
        }
        if (ticket != null) {
            LoginTicket loginTicket = loginTicketDAO.selectByTicket(ticket);
            if (loginTicket == null || loginTicket.getExpired().before(new Date()) || loginTicket.getStatus() != 0) {
                return true;
            }
            User user = userDAO.selectById(loginTicket.getUserId());
            hostHolder.setUser(user);
        }
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        if (modelAndView != null) {
            modelAndView.addObject("user", hostHolder.getUser());
        }
    }

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

新建 config 包,创建 WebConfig 类,继承 WebMvcConfigurer,重写 addInterceptors() 方法注册拦截器。

public class WebConfig implements WebMvcConfigurer {
    @Resource
    PassportInterceptor passportInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(passportInterceptor);
        WebMvcConfigurer.super.addInterceptors(registry);
    }
}

要同时处理多个用户的请求,就要用到多线程,在 model 包建一个 User 多线程的类,成员变量为 ThreadLocal类型。

@Component
public class HostHolder {
    private static final ThreadLocal users = new ThreadLocal<>();

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

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

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

如此,只要成功验证了浏览器 cookie 中的令牌,客户端就可以显示为登录状态了。

参考博客

Spring 过滤器 拦截器 AOP区别

filter、interceptor、aop实现与区别

Filter、Interceptor、Aop实现与区别

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/458812.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号