IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> 【Spring Security】springboot + mybatis-plus + mysql 用户logout登出后token失效 -> 正文阅读

[Java知识库]【Spring Security】springboot + mybatis-plus + mysql 用户logout登出后token失效

在这里插入图片描述

前言

回顾:

【Spring Security】springboot + mybatis-plus + mysql 从数据库读取用户信息验证登录

【Spring Security】springboot + mybatis-plus + mysql 密码加密存储下的数据库用户验证登录

【Spring Security】springboot + mybatis-plus + mysql 用户token验证登录功能

如果使用过spring security的原生/logout接口,可以发现,如果持有之前生效过的token,即使调用/logout的用户登出了,我们还是可以访问需要权限才能调用的其他接口,这是不合理的,用户登出了怎么还可以访问其他接口呢?

因此需要手动试/logout接口调用后能够让用户权限失效,我这里利用了 enabled 字段来控制用户权限。

核心

核心是,令enabled字段成为用户登录的状态字段,当用户login登录时,修改数据表中的用户的enabled字段为true,表示登录状态。

当用户logout登出时,或者token过期时,修改数据表中的用户的enabled字段为false,表示用户已经登出。

@Component
public class UsernamePasswordLogoutSuccessHandler implements LogoutSuccessHandler {

    @Autowired
    private UserService userService;

    @Override
    public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        Object principal = authentication.getPrincipal();
        // 获取当前登录用户名
        UserDetails userDetails = (UserDetails) principal;
        String username = userDetails.getUsername();
        // 用户失效
        userService.userDisenabled(username);

        // 构造一个 UserPattern 返回体返回
        UserPattern userPattern = new UserPattern();
        userPattern.setUsername(username);
        userPattern.setPassword(userDetails.getPassword());
        userPattern.setEnabled(Boolean.FALSE);
        userPattern.setAccountNonExpired(userDetails.isAccountNonExpired());
        userPattern.setAccountNonLocked(userDetails.isAccountNonLocked());
        userPattern.setCredentialsNonExpired(userDetails.isCredentialsNonExpired());

        response.setContentType("application/json;charset=utf-8");
        PrintWriter out = response.getWriter();
        out.write(JSON.toJSONString(userPattern));
        out.flush();
        out.close();
    }
}

由于所有的接口调用都需要通过过滤器进行token鉴定,因此在在JWT过滤器中,增加一个对当前登录用户的判断,如果当前登录用户是enabled=true,则可以继续验证token,如果enabled=false,说明用户已经失效,直接清空token后返回。

@Slf4j
@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {

    private String tokenHead = "Bearer ";

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private UserService userService;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String tokenValue = request.getHeader(HttpHeaders.AUTHORIZATION);
        if (!StringUtils.hasText(tokenValue)){
            filterChain.doFilter(request, response);
            return;
        }
        // 去掉tokenHead留下token
        String token = tokenValue.substring(tokenHead.length());
        Map<String, Object> parseJWT = JWTUtil.parseToken(token);

        // 检查token过期时间
        if (JWTUtil.isExpiresIn((long) parseJWT.get("expiresIn"))){
            // token 已经过期,令token 销毁,账号失效
            SecurityContextHolder.getContext().setAuthentication(null);
            filterChain.doFilter(request, response);
            return;
        }

        String username = (String) parseJWT.get("username");
        // 检查 user 是否 enabled
        if (!userService.isUserEnabled(username)) {
            // 用户不可用,token销毁
            SecurityContextHolder.getContext().setAuthentication(null);
            filterChain.doFilter(request, response);
            return;
        }

        if (StringUtils.hasText(username) && SecurityContextHolder.getContext().getAuthentication() == null){
            // 正常用户
            UserDetails userDetails = userDetailsService.loadUserByUsername(username);
            if(userDetails != null && userDetails.isEnabled()){
                UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());

                authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));

                // 设置用户登录状态
                log.info("authenticated user {}, setting security context", username);
                SecurityContextHolder.getContext().setAuthentication(authentication);
            }
        }
        filterChain.doFilter(request, response);
    }
}

实现

UserService.java

package org.sample.service;

import com.baomidou.mybatisplus.extension.service.IService;
import org.sample.common.request.UserRegisterRequest;
import org.sample.common.response.Response;
import org.sample.dao.entity.UserPattern;

import java.util.List;

/**
 * 用户信息服务接口
 * @author 
 */
public interface UserService extends IService<UserPattern> {

    Response userRegister(UserRegisterRequest userRegisterRequest);

    UserPattern loadByUserName(String username);

    String isAuthorizedUsersExist(List<String> authorizedUsers);

    Response userDisenabled(String username);

    Boolean isUserEnabled(String username);
}

UserServiceImpl.java

package org.sample.service.Impl;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.sample.common.enums.IsDeletedEnum;
import org.sample.common.request.UserRegisterRequest;
import org.sample.common.response.Response;
import org.sample.common.utils.ResponseGenerator;
import org.sample.common.utils.SecurityUtils;
import org.sample.dao.entity.UserPattern;
import org.sample.dao.mapper.UserServiceMapper;
import org.sample.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

import javax.validation.Valid;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.List;

/**
 * 
 * @author 
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserServiceMapper, UserPattern> implements UserService, UserDetailsService {

    @Autowired
    private UserServiceMapper userServiceMapper;

    @Autowired
    private UserService userService;


    /**
     * 用户注册
     * @param userRegisterRequest
     * @return
     */
    @Override
    public Response userRegister(@Valid UserRegisterRequest userRegisterRequest) {
        String username = userRegisterRequest.getUsername();

        // XSS和SQL注入检查,得到过滤后用户名
        String s = SecurityUtils.cleanXSSSql(username);
        // 过滤前和过滤后进行比对
        if (!s.equals(username)) {
            return ResponseGenerator.genFailResponse("非法输入!");
        }

        // 用户名查重
        Integer repeat = userServiceMapper.selectCount(Wrappers.lambdaQuery(UserPattern.class)
                .likeRight(UserPattern::getUsername, username)
                .likeLeft(UserPattern::getUsername, username)
        );
        if (repeat > 0) {
            return ResponseGenerator.genFailResponse("用户名已存在,不可重复注册");
        }

        UserPattern userPattern = new UserPattern();
        userPattern.setUsername(username);
        // 密码加密储存
        String encodedPassword = new BCryptPasswordEncoder().encode(userRegisterRequest.getPassword());
        userPattern.setPassword(encodedPassword);
        userPattern.setEnabled(Boolean.TRUE);
        userPattern.setAccountNonExpired(Boolean.TRUE);
        userPattern.setAccountNonLocked(Boolean.TRUE);
        userPattern.setCredentialsNonExpired(Boolean.TRUE);
        userPattern.setIsDeleted(IsDeletedEnum.NO.getCode());
        userPattern.setCreateTime(new Timestamp(System.currentTimeMillis()));
        userPattern.setCreateUser(username);
        userPattern.setUpdateUser(username);

        // 用户名落库存储
        int insert = userServiceMapper.insert(userPattern);
        if (insert > 0) {
            return ResponseGenerator.genSuccessResponse(userPattern);
        }

        return ResponseGenerator.genFailResponse("网络错误!当前服务不可用,请尝试重试或联系客服");
    }

    /**
     * 方法一
     * spring security方法:auth.userDetailsService(userDetailsService())
     * @param username 用户名
     * @return UserPattern
     */
    @Override
    public UserPattern loadByUserName(String username) {

        // XSS和SQL注入检查
        String s = SecurityUtils.cleanXSSSql(username);
        if (!s.equals(username)) {
            throw new RuntimeException("非法输入!");
        }

        List<UserPattern> userPatterns = userServiceMapper.selectList(Wrappers.lambdaQuery(UserPattern.class)
                .likeLeft(UserPattern::getUsername, username)
                .likeRight(UserPattern::getUsername, username)
                .eq(UserPattern::getIsDeleted, IsDeletedEnum.NO.getCode())
        );
        UserPattern userPattern = userPatterns.get(0);
        return userPattern;
    }

    /**
     * 方法二
     * spring security方法:auth.userDetailsService(userDetailsService)
     * @param username 用户名
     * @return UserDetails
     * @throws UsernameNotFoundException
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        // XSS和SQL注入检查
        String s = SecurityUtils.cleanXSSSql(username);
        if (!s.equals(username)) {
            throw new RuntimeException("非法输入!");
        }

        List<UserPattern> userPatterns = userServiceMapper.selectList(Wrappers.lambdaQuery(UserPattern.class)
                .likeLeft(UserPattern::getUsername, username)
                .likeRight(UserPattern::getUsername, username)
                .eq(UserPattern::getIsDeleted, IsDeletedEnum.NO.getCode())
        );
        try {
            // 如果get不到,就说明库里没有该用户,catch扔异常
            UserPattern userPattern = userPatterns.get(0);
            userPattern.setUpdateTime(new Timestamp(System.currentTimeMillis()));
            userPattern.setEnabled(Boolean.TRUE);
            userService.updateById(userPattern);

            // 如果查不到用户名,这里可以抛出UsernameNotFoundException异常
            // 根据username查询权限,这里假设从任意位置查到权限是auth
            List<GrantedAuthority> authorities = new ArrayList<>();
            authorities.add(new SimpleGrantedAuthority("auth"));
            return new User(userPattern.getUsername(), userPattern.getPassword(), Boolean.TRUE,
                    userPattern.isAccountNonExpired(), userPattern.isCredentialsNonExpired(), userPattern.isAccountNonLocked(), authorities);
        } catch (Exception e) {
            throw new RuntimeException("用户不存在!");
        }
    }

    /**
     * 检查输入的 authorizedUsers 在数据库是否存在
     * @param authorizedUsers 输入的授权用户
     * @return String 如果输入存在,返回空字符串
     */
    @Override
    public String isAuthorizedUsersExist(List<String> authorizedUsers) {
        String result = "";
        // 遍历授权用户列表 authorizedUsers
        for (String username : authorizedUsers) {
            List<UserPattern> userPatterns = userServiceMapper.selectList(Wrappers.lambdaQuery(UserPattern.class)
                    .likeLeft(UserPattern::getUsername, username)
                    .likeRight(UserPattern::getUsername, username)
                    .eq(UserPattern::getIsDeleted, IsDeletedEnum.NO.getCode())
            );
            try {
                // 如果get不到,就说明库里没有该用户,则authorizedUsers中包含无效输入,走catch
                UserPattern userPattern = userPatterns.get(0);
            } catch (Exception e) {
                log.error("输入的授权用户 " + username + " 无效,请重新检查!");
                result += username;
                return result;
            }
        }
        // 如果 authorizedUsers 存在,返回空字符串
        return result;
    }

    /**
     * 使用户失效
     * @param username 用户名
     * @return Response
     */
    @Override
    public Response userDisenabled(String username) {
        // 把用户取出
        List<UserPattern> userPatterns = userServiceMapper.selectList(Wrappers.lambdaQuery(UserPattern.class)
                .likeLeft(UserPattern::getUsername, username)
                .likeRight(UserPattern::getUsername, username)
                .eq(UserPattern::getIsDeleted, IsDeletedEnum.NO.getCode())
        );
        try {
            // 如果get不到,就说明库里没有该用户,catch扔异常
            UserPattern userPattern = userPatterns.get(0);
            userPattern.setUpdateTime(new Timestamp(System.currentTimeMillis()));
            // 用户失效
            userPattern.setEnabled(Boolean.FALSE);
            userService.updateById(userPattern);

            return ResponseGenerator.genSuccessResponse();
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }

        return ResponseGenerator.genFailResponse("网咯错误!");
    }

    /**
     * 判断 user 是否 enabled
     * @param username
     * @return
     */
    @Override
    public Boolean isUserEnabled(String username) {
        // 把用户取出
        List<UserPattern> userPatterns = userServiceMapper.selectList(Wrappers.lambdaQuery(UserPattern.class)
                .likeLeft(UserPattern::getUsername, username)
                .likeRight(UserPattern::getUsername, username)
                .eq(UserPattern::isEnabled, Boolean.TRUE)
                .eq(UserPattern::getIsDeleted, IsDeletedEnum.NO.getCode())
        );
        // 如果userPatterns则为空,则user已失效
        if (userPatterns.isEmpty()) {
            return Boolean.FALSE;
        }
        return Boolean.TRUE;
    }
}


SecurityConfig.java


package org.sample.config;

import org.sample.config.filter.JwtAuthenticationTokenFilter;
import org.sample.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;

/**
 * spring-security 配置类
 * @author 
 */
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserService userService;

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private AuthenticationSuccessHandler authenticationSuccessHandler;

    @Autowired
    private AuthenticationFailureHandler authenticationFailureHandler;

    @Autowired
    private AuthenticationEntryPoint authenticationEntryPoint;

    @Autowired
    private LogoutSuccessHandler logoutSuccessHandler;

    @Autowired
    private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;

    /**
     * PasswordEncoder 是密码加密接口,因为我们是循序渐进的,我这里先用无加密实例
     * @return PasswordEncoder
     */
    @Bean
    PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    /**
     * 实现configure(AuthenticationManagerBuilder auth)配置方法,为了图方便,我先在内存中创建默认用户harry,默认密码是123456,角色是user
     * 配置文件中的默认用户和默认密码注释掉了
     * @param auth
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService);
    }


    /**
     * 通过HttpSecurity 对象,获取到表单登录配置对象,修改对应的用户名和密码参数名称,即可完成自定义用户名和密码参数名称
     * .antMatchers("/swagger-ui.html").permitAll() 等等是给swagger开绿灯,不用登录直接访问
     * .antMatchers("/register").permitAll() 给用户注册接口开绿灯,注册的时候不需要登录
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/swagger-ui.html").permitAll()
                .antMatchers("/webjars/**").permitAll()
                .antMatchers("/swagger-resources/**").permitAll()
                .antMatchers("/v2/*").permitAll()
                .antMatchers("/csrf").permitAll()
                .antMatchers("/").permitAll()
                .antMatchers("/login").permitAll()
                .antMatchers("/login.html").permitAll()
                .antMatchers("/authentication", "/login.html").permitAll()
                .antMatchers("/register").permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .successHandler(authenticationSuccessHandler)
                .failureHandler(authenticationFailureHandler)
                .permitAll()
                .and()
                .exceptionHandling()
                .authenticationEntryPoint(authenticationEntryPoint)
                .and()
                .logout()
                .logoutSuccessHandler(logoutSuccessHandler)
                .deleteCookies("JSESSIONID")
                .invalidateHttpSession(true)
                .permitAll()
                .and().csrf().disable()
        ;
        http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
    }

    public static void main(String[] args) {
    }
}

JwtAuthenticationTokenFilter.java

package org.sample.config.filter;

import lombok.extern.slf4j.Slf4j;
import org.sample.common.utils.JWTUtil;
import org.sample.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Map;

@Slf4j
@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {

    private String tokenHead = "Bearer ";

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private UserService userService;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String tokenValue = request.getHeader(HttpHeaders.AUTHORIZATION);
        if (!StringUtils.hasText(tokenValue)){
            filterChain.doFilter(request, response);
            return;
        }
        // 去掉tokenHead留下token
        String token = tokenValue.substring(tokenHead.length());
        Map<String, Object> parseJWT = JWTUtil.parseToken(token);
        String username = (String) parseJWT.get("username");

        // 检查token过期时间
        if (JWTUtil.isExpiresIn((Long) parseJWT.get("expiresIn"))){
            // 用户失效
            userService.disenabledUser(username);
            // token 已经过期,令token 销毁,账号失效
            SecurityContextHolder.getContext().setAuthentication(null);
            filterChain.doFilter(request, response);
            return;
        }

        // 检查 user 是否 enabled
        if (!userService.isUserEnabled(username)) {
            // 用户不可用,token销毁
            SecurityContextHolder.getContext().setAuthentication(null);
            filterChain.doFilter(request, response);
            return;
        }

        if (StringUtils.hasText(username) && SecurityContextHolder.getContext().getAuthentication() == null){
            // 正常用户
            UserDetails userDetails = userDetailsService.loadUserByUsername(username);
            if(userDetails != null && userDetails.isEnabled()){
                UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());

                authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));

                // 设置用户登录状态
                log.info("authenticated user {}, setting security context", username);
                SecurityContextHolder.getContext().setAuthentication(authentication);
            }
        }
        filterChain.doFilter(request, response);
    }
}

UsernamePasswordLogoutSuccessHandler.java

package org.sample.handler;

import com.alibaba.fastjson.JSON;
import org.sample.dao.entity.UserPattern;
import org.sample.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.stereotype.Component;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * 
 * @author
 */
@Component
public class UsernamePasswordLogoutSuccessHandler implements LogoutSuccessHandler {

    @Autowired
    private UserService userService;

    @Override
    public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        Object principal = authentication.getPrincipal();
        // 获取当前登录用户名
        UserDetails userDetails = (UserDetails) principal;
        String username = userDetails.getUsername();
        // 用户失效
        userService.userDisenabled(username);

        // 构造一个 UserPattern 返回体返回
        UserPattern userPattern = new UserPattern();
        userPattern.setUsername(username);
        userPattern.setPassword(userDetails.getPassword());
        userPattern.setEnabled(Boolean.FALSE);
        userPattern.setAccountNonExpired(userDetails.isAccountNonExpired());
        userPattern.setAccountNonLocked(userDetails.isAccountNonLocked());
        userPattern.setCredentialsNonExpired(userDetails.isCredentialsNonExpired());

        response.setContentType("application/json;charset=utf-8");
        PrintWriter out = response.getWriter();
        out.write(JSON.toJSONString(userPattern));
        out.flush();
        out.close();
    }
}

UserPattern.java

package org.sample.dao.entity;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;

import java.io.Serializable;
import java.sql.Timestamp;


/**
 * user用entity
 * @author 
 */
@Data
@EqualsAndHashCode(callSuper = false)
@NoArgsConstructor
@AllArgsConstructor
@TableName(value = "test.image_management_user")
public class UserPattern implements Serializable {

    /**
     * ID,自增,主键
     */
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;

    /**
     * 用户名 userName
     */
    @TableField(value = "username")
    private String username;

    /**
     * 密码 password
     */
    @TableField(value = "passwd")
    private String password;

    @TableField(value = "enabled")
    private boolean enabled;

    @TableField(value = "account_non_expired")
    private boolean accountNonExpired;

    @TableField(value = "account_non_locked")
    private boolean accountNonLocked;

    @TableField(value = "credentials_non_expired")
    private boolean credentialsNonExpired;

    /**
     * 是否删除
     */
    @TableField(value = "is_deleted")
    private int isDeleted;

    /**
     * 创建时间
     */
    @TableField(value = "create_time")
    private Timestamp createTime;

    /**
     * 创建用户
     */
    @TableField(value = "create_user")
    private String createUser;

    /**
     * 更新时间
     */
    @TableField(value = "update_time")
    private Timestamp updateTime;

    /**
     * 更新用户
     */
    @TableField(value = "update_user")
    private String updateUser;

}

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-06-29 18:51:47  更:2022-06-29 18:52:04 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 16:31:28-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码