• 【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();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    由于所有的接口调用都需要通过过滤器进行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);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57

    实现

    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);
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    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;
        }
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232

    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) {
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113

    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);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81

    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();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53

    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;
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
  • 相关阅读:
    HTML中的<!DOCTYPE html>和JavaScript中的strict mode
    (173)FPGA约束:单周期时序分析或默认时序分析
    ESP8266-Arduino编程实例-TSL2591数光转换器驱动
    IBM websphere通道联通搭建和测试
    阶段六-Day01-Linux入门
    Java面试题总结2023
    光伏电池局部遮阴下三种不同的工况对比MATLAB仿真模型
    ElasticSearch-查询语法(结构化查询)
    springboot+redis
    std::call_once
  • 原文地址:https://blog.csdn.net/fisherish/article/details/125297531