Use of Session and JWT in projects

Use of Session and JWT in projects

This blog post will introduce and display the code of the two ways of interaction between the client and the server based on the method of session and JWT used in javaWeb and several projects for user authentication.

The use of Session in the project

1. Use Session for user login verification

The code is just an example, for reference only.
There are also database connections and yml file configurations.
This blog post is only a brief introduction, and it is also about reviewing and consolidating the knowledge of login authentication

1. First, the user class [Mapper will not be introduced anymore]

@Data
@EqualsAndHashCode(callSuper = false)
@ApiModel(value="User对象", description="用户表")
public class User implements Serializable {
    
    

    private static final long serialVersionUID = 1L;

    @ApiModelProperty(value = "用户id")
    @TableId
    private Long userId;

    @ApiModelProperty(value = "用户名称")
    private String username;

    @ApiModelProperty(value = "电话")
    private String phone;

    @ApiModelProperty(value = "密码")
    private String password;

    @ApiModelProperty(value = "如果用户传入头像,则用用户的;如果没传,则默认")
    private String avatar;

    @ApiModelProperty(value = "用户介绍")
    private String description;

    @ApiModelProperty(value = "普通用户是0,管理员是1")
    private Integer roles;

    @ApiModelProperty(value = "创建时间")
    private LocalDateTime createTime;

    @ApiModelProperty(value = "修改时间")
    private LocalDateTime updateTime;

    @ApiModelProperty(value = "逻辑删除字段:0 未删除,1 删除")
    private Integer isDelete;


}

2. Create login and logout interfaces

	/**
     * 登录
     *
     * 这里需要说明的是:
     * UserQueryResp 是特殊返回类,包含指定返回内容
     * UserLoginReq  是特殊请求类,包含指定请求内容
     */
    UserQueryResp login(UserLoginReq req, HttpSession session);
	/**
     * 注销
     *
     * @param session 会话
     */
    void logOut(HttpSession session);

3. Write code in the implementation class of the login interface

Today's topic is Session, so in the code
session.setAttribute(SESSION_KEYWORDS, userSession);
is to set the current user's Session. When the user logs in, after passing the interceptor [written below], the server will generate a session and return the SeesionID to the browser.

/**
 * <p>
 * 用户表 服务实现类
 * </p>
 *
 * @author yzh
 * @since 2022-08-13
 */
@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    
    

    @Resource
    private UserMapper userMapper;

    @Resource
    private CommonService commonService;

	/**
     * 登录
     * yzh
     * 代码中使用了
     * 1、MyBatis-Plus的条件查询
     * 2、自定义异常 BusinessException
     * 3、枚举类-异常值 BusinessCode
     * 4、Md5 加密处理  DigestUtils.md5DigestAsHex()
     * 5、自定义拷贝类  CopyUtil.copy【类似于BeanUtils的copyproperties方法】
     */
    @Override
    public UserQueryResp login(UserLoginReq req, HttpSession session) {
    
    
        //1、 根据phone查询数据库是否有此用户
        User userDb = this.getOne(Wrappers.lambdaQuery(User.class).eq(User::getPhone, req.getPhone()));
        // 如果数据库中无此用户
        if (ObjectUtils.isEmpty(userDb)){
    
    
            // 前端显示错误原因
            throw new BusinessException(BusinessCode.LOGIN_ERROR,BusinessCode.LOGIN_ERROR.getMessage());
        }

        // 2、对比判断输入密码是否正确
        String reqPwd = DigestUtils.md5DigestAsHex((req.getPassword() + SALT).getBytes());
        if (!reqPwd.equals(userDb.getPassword())){
    
    
            throw new BusinessException(BusinessCode.LOGIN_ERROR,BusinessCode.LOGIN_ERROR.getMessage());
        }
        // 3、将当前用户信息存入session
        User userSession = new User();
        userSession.setUserId(userDb.getUserId());// 拦截器要用到
        session.setAttribute(SESSION_KEYWORDS,userSession);

        return CopyUtil.copy(userDb,UserQueryResp.class);

    }

    /**
     * 退出登录
     *
     * 退出登录,直接调用HttpSession的removeAttribute()方法
     */
    @Override
    public void logOut(HttpSession session) {
    
    
        session.removeAttribute(SESSION_KEYWORDS);
    }
 }

4. Write code in the login controller Controller

In the Controller code, the logical operation of the implementation class is called to provide an interface for the front-end call

/**
*yzh
*/
@RestController
@RequestMapping("/user")
@Api(tags = "用户接口")
public class UserController {
    
    

    @Resource
    private UserService userService;

	/**
	* @Valid校验
	* @RequestBody  适应JSON格式
	* CommonResponse 自定义的通用返回类
	*/
	@PostMapping("/login")
    @ApiOperation("用户登录")
    public CommonResponse<UserQueryResp> login(@RequestBody @Valid UserLoginReq req, HttpSession session){
    
    
        return CommonResponse.success(userService.login(req,session),"用户登录成功!");
    }


    @PostMapping("/logout")
    @ApiOperation("退出登录")
    public CommonResponse<String> logOut(HttpSession session){
    
    
        userService.logOut(session);
        return CommonResponse.success("用户退出登录成功!");
    }
}    

5. Write code in the interceptor

HttpSession session = request.getSession();
User sessionUser = (User) session.getAttribute(SESSION_KEYWORDS);
This code is to obtain the current user through the Session and determine the unique user

/**
 * 登录拦截器
 *
 * @author yzh
 * @since 2022/8/16
 */
@Component
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {
    
    
    @Resource
    private UserMapper userMapper;

    /**
     * 前处理  进行拦截处理
     *
     * HttpSession session = request.getSession();
       User sessionUser = (User) session.getAttribute(SESSION_KEYWORDS);
       这段代码就是通过Session来获取当前用户,确定唯一用户
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    
    
        log.info("-------start interceptor------");

        // 通过session得到当前用户,进行操作
        HttpSession session = request.getSession();
        User sessionUser = (User) session.getAttribute(SESSION_KEYWORDS);
        if (ObjectUtils.isEmpty(sessionUser)){
    
    
            throw new BusinessException(BusinessCode.USER_MESSAGE_ERROR,"此用户未登录!");
        }
        // 将处理程序封装为HandlerMethod
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        // handlerMethod.hasMethodAnnotation(AdminRole.class)返回boolean值
        // 当返回 true 时,进行鉴别操作
        /*if (sessionUser.getIsDelete()==1){
            throw new BusinessException(BusinessCode.USER_MESSAGE_ERROR,"此用户已被注销,请重试!");
        }*/

        if (handlerMethod.hasMethodAnnotation(AdminRole.class)){
    
    
            // 通过session传入的Id来匹配当前用户信息,得到当前用户对象
            User user = userMapper.selectById(sessionUser.getUserId());
            if (ObjectUtils.isEmpty(user)){
    
    
                throw new BusinessException(BusinessCode.USER_MESSAGE_ERROR,"此用户不存在!");
            }
            if (!user.getRoles().equals(ADMIN_ROLE)){
    
    
                throw new BusinessException(BusinessCode.USER_MESSAGE_ERROR,"此用户无权限!");
            }
        }
        return true;
    }
}

Session is also used in other modules, but only so much is displayed.
Like user update, purchase of items, etc., it is necessary to verify whether it is the current user through Session

6. Some of the above classes

CommonResponse

/**
 * 公共返回类
 *
 * @author yzh
 * @date 2022/08/15
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class CommonResponse<T> implements Serializable {
    
    
    private static final long serialVersionUID = 112345679815613L;

    private boolean success;

    private T data;

    private String message;

    /**
     * 成功
     *
     * @return {@link CommonResponse}<{@link T}>
     */
    public static <T> CommonResponse<T> success(T data, String message) {
    
    
        return new CommonResponse<>(true, data, message);
    }

    /**
     * 成功
     *
     * @return {@link CommonResponse}<{@link T}>
     */
    public static <T> CommonResponse<T> success(String message) {
    
    
        return new CommonResponse<>(true, null, message);
    }

    /**
     * 成功
     *
     * @return {@link CommonResponse}<{@link T}>
     */
    public static <T> CommonResponse<T> success() {
    
    
        return new CommonResponse<>(true, null, "");
    }

    /**
     * 错误
     *
     * @param code    代码
     * @param message 消息
     * @return {@link CommonResponse}<{@link Object}>
     */
    public static CommonResponse<Object> error(BusinessCode code, String message) {
    
    
        return new CommonResponse<>(false, code.getMessage(), message);
    }


}

BusinessException

/**
 * 业务异常
 *
 * @author yzh
 * @date 2022/08/15
 */
public class BusinessException extends RuntimeException {
    
    

    private final BusinessCode businessCode;
    private final String message;

    public BusinessException(BusinessCode businessCode, String message) {
    
    
        this.businessCode = businessCode;
        this.message = message;
    }

    @Override
    public String getMessage() {
    
    
        return message;
    }

    public BusinessCode getBusinessCode() {
    
    
        return businessCode;
    }

    @Override
    public Throwable fillInStackTrace() {
    
    
        return this;
    }
}

BusinessCode

/**
 * 业务代码
 *
 * @author yzh
 * @date 2022/08/15
 */
public enum BusinessCode {
    
    

    PARAM_ERROR("参数错误"),

    LOGIN_ERROR("账号或密码错误"),

    USER_MESSAGE_ERROR("此用户信息错误"),

    AUTH_ERROR("权限错误"),

    FILE_ERROR("文件异常"),

    ASSESS_ERROR("评价异常"),

    BUY_ERROR("购买异常"),

    PRODUCT_ERROR("商品异常")

    ;

    private final String message;

    BusinessCode(String message) {
    
    
        this.message = message;
    }

    public String getMessage() {
    
    
        return message;
    }
}

CopyUtil

package com.yzh.utils;

import org.springframework.beans.BeanUtils;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.List;

/**
 * 复制工具类
 *
 * @author yzh
 * @since 2022/08/05
 */
public class CopyUtil {
    
    

    /**
     * 单体复制
     *
     * @param source 源
     * @param clazz  clazz
     * @return {@link T}
     */
    public static <T> T copy(Object source, Class<T> clazz) {
    
    
        if (source == null) {
    
    
            return null;
        }
        T obj;
        try {
    
    
            obj = clazz.getDeclaredConstructor().newInstance();
        } catch (Exception e) {
    
    
            e.printStackTrace();
            return null;
        }
        BeanUtils.copyProperties(source, obj);
        return obj;
    }

    /**
     * 列表复制
     *
     * @param source 源
     * @param clazz  clazz
     * @return {@link List}<{@link T}>
     */
    public static <T> List<T> copyList(List<?> source, Class<T> clazz) {
    
    
        List<T> target = new ArrayList<>();
        if (!CollectionUtils.isEmpty(source)) {
    
    
            for (Object c : source) {
    
    
                T obj = copy(c, clazz);
                target.add(obj);
            }
        }
        return target;
    }
}

The use of JWT in the project

1. Simply use JWT

1. Create a new entity class

@AllArgsConstructor
@NoArgsConstructor
@Data
public class User {
    
    
    private Integer id;
    private String name;
    private String password;
}

2. dao layer

@Mapper
public interface UserDao {
    
    

    /**
     * 登录方法
     * @param user
     * @return
     */
    User login(User user);
}


3. UserDao.xml file

Note: The path must be correct

The path set in application.properties should also be correct

mybatis.type-aliases-package=com.lut.entity
mybatis.mapper-locations=classpath:mapper/*.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.lut.dao.UserDao">

    <select id="login"  parameterType="User" resultType="User">
        select id,name,password from user where name=#{name} and password=#{password}
    </select>

</mapper>

4. service layer

@Service
public interface UserService {
    
    
    /**
     * 登录方法
     * @param user
     * @return
     */
    User login(User user);
}


@Service
@Transactional
public class UserServiceImpl implements UserService {
    
    

    @Autowired
    private UserDao userDao;


    @Override
    public User login(User user) {
    
    
        //接受用户查询数据库
        User userDB = userDao.login(user);

        //查询到这个用户就返回,没有则抛出错误
        if (userDB != null) {
    
    
            return userDB;
        }else{
    
    
            throw new RuntimeException("登录失败!");
        }
    }


}

5. Controller layer

@RestController
@Slf4j
public class UserController {
    
    

    @Autowired
    private UserService userService;

    @GetMapping("/test")
    public String test(User user) {
    
    
        return "ok";
    }


    @GetMapping("/user/login")
    public Map<String, Object> login(User user) {
    
    
        log.info("用户名:[{}]", user.getName());
        log.info("密码:[{}]", user.getPassword());
        HashMap<String, Object> map = new HashMap<>();

        try {
    
    
            //获取user对象
            User userDB = userService.login(user);
            //将user对象添加到map
            Map<String, String> payload = new HashMap<>();
            payload.put("id", userDB.getId().toString());
            payload.put("name", userDB.getName());
            //生成令牌
            String token = JWTUtils.getToken(payload);
            
            map.put("state", true);
            map.put("msg", "认证成功");
            map.put("token", token);

        } catch (Exception e) {
    
    
            map.put("state", false);
            map.put("msg", e.getMessage());
        }

        return map;
    }
    

    @PostMapping("/user/test")
    public Map<String, Object> test(String token) {
    
    
        Map<String, Object> map = new HashMap<>();
        log.info("当前token为:[{}]", token);

        try {
    
    
            DecodedJWT verify = JWTUtils.verify(token);
            map.put("state", true);
            map.put("msg", "请求成功");
            return map;
        } catch (SignatureVerificationException e) {
    
    
            map.put("msg", "签名不一致异常");
            e.printStackTrace();
        } catch (TokenExpiredException e) {
    
    
            map.put("msg", "令牌过期异常");
            e.printStackTrace();
        } catch (AlgorithmMismatchException e) {
    
    
            map.put("msg", "算法不匹配异常");
            e.printStackTrace();
        } catch (Exception e) {
    
    
            map.put("msg", "token无效");
            e.printStackTrace();
        }

        map.put("state", false);
        return map;
    }


}

6. Test

You can use ApiFox or Postman

7. Interceptor

Decide what to block

code is just for demonstration

@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
    
    

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
    
    
        //只做示例
        registry.addInterceptor(new JWTInterceptor())
                .addPathPatterns("/user/test") //其他接口保护
                .excludePathPatterns("/user/login");//所有用户接口放行

    }
}


Intercept before processing the path


public class Login	Interceptor implements HandlerInterceptor {
    
    

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    
    

        Map<String,Object> map=new HashMap<>();
        //获取请求头中的令牌
        String token=request.getHeader("token");

        try {
    
    
            //验证令牌
            JWTUtils.decodedJWT(token);
            //放行请求
            return true;
        } catch (SignatureVerificationException e) {
    
    
            map.put("msg", "签名不一致异常");
            e.printStackTrace();
        } catch (TokenExpiredException e) {
    
    
            map.put("msg", "令牌过期异常");
            e.printStackTrace();
        } catch (AlgorithmMismatchException e) {
    
    
            map.put("msg", "算法不匹配异常");
            e.printStackTrace();
        } catch (Exception e) {
    
    
            map.put("msg", "token无效");
            e.printStackTrace();
        }

        map.put("state",false);
        //将map 转为 json
        String json=new ObjectMapper().writeValueAsString(map);
        response.setContentType("appliaction/json;charset=UTF-8");
        return false;

    }
}

Test results
It is best to encapsulate the token in the header

2. Combining Spring Security user authentication and authority management

no yet

Guess you like

Origin blog.csdn.net/AN_NI_112/article/details/131502664