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

Community项目--用户的登录

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

Community项目--用户的登录

用户的登录

文章目录

用户的登录

1. 验证码的生成

1.1. 生成验证码工具的介绍1.2. 导入jar包1.3. 编写Kaptcha配置类1.4. 生成随机字符和图片 2. 登录和退出功能实现

2.1. 登录

2.1.1. 业务和需求2.1.2. 功能实现 2.2. 退出 3. 显示登录信息

3.1. 业务和需求3.2. 拦截器(Interceptor)3.3. ThreadLocal3.4. 功能实现

3.4.1. 拦截器的应用3.4.2. 定义拦截器3.4.3. 配置拦截器

1. 验证码的生成 1.1. 生成验证码工具的介绍

官网地址: https://code.google.com/archive/p/kaptch(需科学上网)
Kaptcha是一个容易并且安全的生成验证码的模块化框架,用户可以通过几个配置选项来改变验证码输出的外观。

1.2. 导入jar包
		
            com.github.penggle
            kaptcha
            2.3.2
        
1.3. 编写Kaptcha配置类
@Configuration
public class KaptchaConfig {

    @Bean
    public Producer kaptchaProducer(){
        Properties properties = new Properties();
        // 图片宽
        properties.setProperty("kaptcha.img.width", "100");
        // 图片高
        properties.setProperty("kaptcha.img.height", "40");
        // 字体大小
        properties.setProperty("kaptcha.textproducer.font.size", "32");
        // 字体颜色
        properties.setProperty("kaptcha.textproducer.font.color", "black");
        // 文本集合
        properties.setProperty("kaptcha.textproducer.char.string", "0123456789ABCDEFGJHIJKLMNOPQRSTUVWXYZ");
        // 验证码长度
        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;
    }
}
1.4. 生成随机字符和图片

LoginController

    @Autowired
    private Producer kaptchaProducer;

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

    @GetMapping("/kaptcha")
    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); // ImageIO 用于定位图片的读和写的工具类,并执行简单的编码和解码
        } catch (IOException e) {
            logger.error("响应验证码失败: " + e.getMessage());
        }
    }

login.html



结果展示

2. 登录和退出功能实现

2.1. 登录 2.1.1. 业务和需求

用户登录时,服务端会验证从客户端接受的账号,密码和验证码是否正确.如果成功,标记账户状态为有效,生成登录凭证,并返回给客户端,失败时则跳回登录页重新登录.同时,如果用户选择记住我,则用户可以在较长的一段时间不需要重新登录.

2.1.2. 功能实现

util类:

    
    int DEFAULT_EXPIRED_SEConDS = 3600 * 12;

    
    int REMEMBER_EXPIRED_SEConDS = 3600 * 24 * 100;

LoginController:

	@Value("${server.servlet.context-path}")
    private String contextPath;
 	
	@PostMapping("/login")
    public String login(String username, String password, String code, boolean remember,
                        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 = remember ? REMEMBER_EXPIRED_SEConDS : DEFAULT_EXPIRED_SECONDS; // 判断是否选择记住我
        Map map = userService.login(username, password, expiredSeconds);
        if (map.containsKey("ticket")) {
            cookie cookie = new cookie("ticket", map.get("ticket").toString());
            cookie.setPath(contextPath);
            cookie.setMaxAge(expiredSeconds);
            response.addcookie(cookie);
            return "redirect:/index";
        } else {
            model.addAttribute("usernameMsg", map.get("usernameMsg"));
            model.addAttribute("passwordMsg", map.get("passwordMsg"));
            return "site/login";
        }
    }

UserService

	
    public Map login(String username, String password, long expiredSeconds) {
        Map map = new HashMap();
        
        // 空值处理
        if (StringUtils.isBlank(username)) {
            map.put("usernameMsg", "账号不能为空");
            return map;
        }
        if (StringUtils.isBlank(password)) {
            map.put("passwordMsg", "密码不能为空");
            return map;
        }
        // 验证账号
        User u = userMapper.selectByName(username);
        if (u == null) {
            map.put("usernameMsg", "该账号不存在");
            return map;
        }
        // 验证是否激活
        if (u.getStatus() == 0) {
            map.put("usernameMsg", "该账号未激活");
            return map;
        }
        // 验证密码
        password = CommunityUtil.md5(password + u.getSalt());
        if (!u.getPassword().equals(password)) {
            map.put("passwordMsg", "密码不正确");
            return map;
        }

        // 生成登录凭证
        LoginTicket loginTicket = new LoginTicket();
        loginTicket.setUserId(u.getId()).setTicket(CommunityUtil.generateUUID()).setStatus(0).setExpired(new Date(System.currentTimeMillis() + expiredSeconds * 1000)); // generateUUID()自定义工具方法,可以生成随机字符串
        loginTicketMapper.insertLoginTicket(loginTicket);

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

        return map;
    }

UserMapper

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

login.html(截取)

xxxxxxx!
2.2. 退出

标记账户状态为无效(略)

3. 显示登录信息 3.1. 业务和需求

用户可以在客户端看到当前登录的信息

3.2. 拦截器(Interceptor)

拦截器是动态拦截Action调用的对象,它提供了一种机制,可以使开发者自定义在一个action的前后执行指定代码,也可以在一个action执行之前阻止其执行。同时,拦截器还可以将通用的代码模块化并作为可重用的类。拦截器中的3个方法
preHandler:在进入Handler方法之前执行此方法,适用于身份认证、身份授权、登陆校验等,比如:在身份认证时,判断到用户没有登陆,则不再向下执行,返回值为false,即可实现拦截,否则,返回true时,不执行拦截;
postHandler:进入Handler方法之后执行此方法,返回ModelAndView之前执行,适用场景从ModelAndView参数出发,比如:将公用的模型数据在这里传入到视图,也可以统一指定显示的视图等;
afterCompletion:在执行Handler完成后执行此方法,适用于统一的异常处理、日志处理等。
(摘自:作者:luckgem 链接:https://www.jianshu.com/p/86a7243a78c9 来源:简书) 3.3. ThreadLocal

多线程访问同一个共享变量的时候容易出现并发问题,特别是多个线程对一个变量进行写入的时候,为了保证线程安全,一般使用者在访问共享变量的时候需要进行额外的同步措施才能保证线程安全性。ThreadLocal是除了加锁这种同步方式之外的一种保证一种规避多线程访问出现线程不安全的方法,当我们在创建一个变量后,如果每个线程对其进行访问的时候访问的都是线程自己的变量这样就不会存在线程不安全问题。ThreadLocal是JDK包提供的,它提供线程本地变量,如果创建一个ThreadLocal变量,那么访问这个变量的每个线程都会有这个变量的一个副本,在实际多线程操作的时候,操作的是自己本地内存中的变量,从而规避了线程安全问题。
(摘自:https://www.cnblogs.com/fsmly/p/11020641.html) 3.4. 功能实现 3.4.1. 拦截器的应用

在请求开始时查询登录用户在本次请求中持有用户数据在模板视图上显示用户数据在请求结束时清理用户数据 3.4.2. 定义拦截器

HostHolder

@Component
public class HostHolder {

    private ThreadLocal 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中获取凭证
        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.findUserById(loginTicket.getUserId());
                // 在本次请求中持有用户 考虑到多线程并发 线程隔离用ThreadLocal
                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();
    }
}
3.4.3. 配置拦截器

WebMvcConfig

 	 @Override
    public void addInterceptors(InterceptorRegistry registry) {
    	
        registry.addInterceptor(loginTicketInterceptor)
                .excludePathPatterns("*.css", "*.js", "*.png", "*.jpg", "*.jpeg");
    }
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/737279.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

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

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