Spring Boot + Thymeleaf 实现分布式登录功能

简单实现思路

1.用户登录成功之后,JWTCreator按照一定规则编码生成一个token,token中包含用户标识以及设定的失效时间,token存储到Cookie中

2.将用户信息存到Redis中,redis设定的key包含用户标识符

3.自定义注解,每一个需要用户验证的控制器接口、页面添加此注解

下面是具体实现

创建一个TokenUti类

package com.demo.manage.common.tools.utils.jwt;


import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;

import javax.servlet.http.HttpServletRequest;
import java.util.Calendar;
import java.util.Map;

/**
 * @author qiqiyu
 * @description
 * @date 2022/4/28
 */
public class TokenUtil {

    public static final String signature = "demo";

    public static final String subject = "demo";


    /**
     * create token
     * @param hashMap
     * @return
     */
    public static String createToken(Map<String, String> hashMap){
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.DATE,1);
        JWTCreator.Builder builder = JWT.create();
        hashMap.forEach((k,v)->builder.withClaim(k,v));
        String token = builder.withExpiresAt(calendar.getTime()).withSubject(subject).sign(Algorithm.HMAC256(signature));
        return token;

    }

    /**
     * verify token
     * @param token
     * @return
     */
    public static DecodedJWT verifyToken(String token){
        return JWT.require(Algorithm.HMAC256(signature)).build().verify(token);
    }

    /**
     * decode token by verify
     * @param token
     * @return
     */
    public static DecodedJWT decodedToken(String token){
        DecodedJWT decodedJWT = null;
        try{
             decodedJWT = verifyToken(token);
        } catch (Exception e){
        } finally {
            return decodedJWT;
        }

    }





}

创建自定义注解以及Handler

package com.demo.manage.common.annotation;

import java.lang.annotation.*;

/**
 * @author qiqiyu
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface LoginCheck {
}
package com.demo.manage.common.config;

import com.demo.manage.common.annotation.LoginCheck;
import com.demo.manage.common.constant.RedisConstants;
import com.demo.manage.common.utils.RedisUtil;
import com.demo.manage.common.utils.ServletUtil;
import com.demo.manage.web.domain.Members;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

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

/**
 * @author qiqiyu
 * @description
 * @date 2021/12/7
 */
public class LoginInterceptorConfig implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        RedisUtil redisUtil = WebApplicationContextUtils.getRequiredWebApplicationContext(request.getServletContext()).getBean(RedisUtil.class);
        //判断请求的方法上是否有注解
        boolean haveAnnotation = handler.getClass().isAssignableFrom(HandlerMethod.class);
        String url = request.getRequestURI();
        if (haveAnnotation) {
            // 强转
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            // 获取方法
            Method method = handlerMethod.getMethod();
          
            if (!method.isAnnotationPresent(LoginCheck.class)) {
                return true;
            }
            //如果有注解,判断是否是MyAnnotation
            LoginCheck ma = method.getAnnotation(LoginCheck.class);
            //如果存在该注解
            if (null != ma) {
                //获得名为member的对象
                Members members = ServletUtil.getLoginMember();
                if (null == members) {
                    //如果不是转发到/index上
                    response.sendRedirect("/login?frontUrl="+url);
                    return false;
                }
               
            }
        }
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    }

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

}

ServletUtil中的实现

public static Members getLoginMember() {
    String token = getCookie("token");
    if (null == token) {
        return null;
    }
    DecodedJWT dj = TokenUtil.decodedToken(token);
    if(null == dj){
        return null;
    }
    String userKey= dj.getClaim("userKey").asString();
    if (StringUtil.isNotBlank(userKey)) {
        Object o = redisUtil.get("member:demo" + userKey);
        if (null != o) {
            Members members = (Members) o;
            return members;
        }
    }
    return null;
}
// 获取cookie中的属性值

public static String getCookie(String var) {
    Cookie[] cookies = getRequest().getCookies();
    if (cookies != null) {
        for (Cookie cookie : cookies) {
            if (cookie.getName().equals(var)) {
                return cookie.getValue();
            }
        }
    }
    return null;
}

登录实现

@PostMapping("/login")
@ResponseBody
public R login(String phone, String password,HttpServletResponse response) {
        Map mapMember = new HashMap(1);
        if (StringUtil.isBlank(memberVo.getPhone())) {
            return R.warn("请输入账号");
        }
        //获取用户信息
        Members members = membersMapper.selectMembersByPhone(phone);
  
            if (StringUtil.isEmpty(password)) {
                return R.warn("请输入密码");
            }
            if (Objects.isNull(members))  {
                return R.warn("用户不存在");
            } 
             //校验密码
            if (!PasswordUtil.checkPassWord(password, members.getPassword())) {
                    return R.warn("用户名或密码不正确");
                } 
             mapMember.put("userKey", members.getUserKey());
             String token = TokenUtil.createToken(mapMember);
             redisUtil.set("member:demo" + members.getUserKey(), members, 3600);
             Cookie cookie = new Cookie("token", value);
             cookie.setMaxAge(3600);
             cookie.setPath("/");
             response.addCookie(cookie);
             return R.ok();       
  
    }

自定义接口的使用

@LoginCheck
@GetMapping("/demo-page")
public String demoPage(ModelMap mmp) {
        return "/demo/page";
    }

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Spring Boot是一个基于Spring框架的快速开发框架,通过提供一系列的开箱即用的功能和优化配置,简化了Java后台应用的开发流程。 Thymeleaf是一个Java模板引擎,用于在服务端渲染HTML页面。它可以和Spring Boot结合使用,通过在HTML页面中使用Thymeleaf的语法,实现页面的动态渲染和数据绑定。 Layui是一个国内比较流行的前端UI框架,提供了大量的CSS样式和JavaScript组件,可以快速构建美观、响应式的前端界面。 Apache Shiro是一个强大的开源安全框架,可以用于认证、授权和加密操作。它提供了对用户身份验证、角色和权限管理的支持,可以帮助开发者快速实现应用的安全控制。 Redis是一个高性能的内存数据库,常用于缓存和存储数据。它支持多种数据结构和操作,可以用于实现分布式锁、消息队列等功能,提高系统的性能和可扩展性。 MyBatis Plus是一个基于MyBatis框架的增强工具,提供了更简单、更便捷的数据库操作方式。它通过代码生成器和一系列的增强功能,简化了数据层的开发工作,提高了开发效率。 综上所述,可以使用Spring Boot作为后台框架,集成Thymeleaf实现页面渲染和数据绑定,使用Layui构建前端界面,使用Apache Shiro进行安全控制,使用Redis进行数据缓存和存储,使用MyBatis Plus进行数据库操作。这样搭建的后台系统可以实现高效、安全、可扩展的功能

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值