• SpringBoot3 整合SpringSecurity


    Maven

      <parent>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-parentartifactId>
            <version>3.2.2version>
            <relativePath/> 
        parent>
         <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-webartifactId>
            dependency>
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-aopartifactId>
            dependency>
    
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-securityartifactId>
            dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    JwtFilter

    package com.system.security;
    
    import cn.hutool.core.util.StrUtil;
    import com.auth0.jwt.interfaces.DecodedJWT;
    import com.system.common.exception.ServerException;
    import com.system.common.utlis.jwt.JwtUtils;
    import com.system.common.utlis.result.Prefix;
    import jakarta.annotation.Resource;
    import jakarta.servlet.FilterChain;
    import jakarta.servlet.ServletException;
    import jakarta.servlet.http.HttpServletRequest;
    import jakarta.servlet.http.HttpServletResponse;
    import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
    import org.springframework.security.core.context.SecurityContextHolder;
    import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
    import org.springframework.stereotype.Component;
    import org.springframework.web.filter.OncePerRequestFilter;
    
    import java.io.IOException;
    
    /**
     * @author kuaiting
     */
    @Component
    public class JwtFilter extends OncePerRequestFilter {
        @Resource
        private JwtUtils jwtUtils;
    
        @Override
        protected void doFilterInternal(HttpServletRequest request,
                                        HttpServletResponse response,
                                        FilterChain filterChain) throws ServletException, IOException {
            // 实现过滤逻辑
            String authorization = request.getHeader(Prefix.TOKEN_KEY);
            System.out.println(authorization);
            if (authorization != null && authorization.startsWith(Prefix.TOKEN_BEARER)) {
                // 验证JWT并设置用户信息到SecurityContextHolder中
                DecodedJWT jwt = jwtUtils.resolveToken(authorization);
                if (jwt != null) {
                    Long uid = jwtUtils.getUid(jwt);
                    SecurityUser details = jwtUtils.getUserDetails(jwt);
                    UsernamePasswordAuthenticationToken authentication =
                            new UsernamePasswordAuthenticationToken(details, null, details.getAuthorities());
                    authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                    SecurityContextHolder.getContext().setAuthentication(authentication);
                    request.setAttribute("uid", uid);
                }
            }
            filterChain.doFilter(request, response);
        }
    
    
        public void validate(HttpServletRequest request) throws ServerException {
            //请求中传来的验证码
            String code = request.getParameter("code");
            String sessionCode = request.getSession().getAttribute("session_code").toString();
            if (StrUtil.isEmpty(code)) {
                throw new ServerException("验证码不能为空!");
            }
            if (StrUtil.isEmpty(sessionCode)) {
                throw new ServerException("验证码已经失效!");
            }
            if (!sessionCode.equalsIgnoreCase(code)) {
                throw new ServerException("验证码输入错误!");
            }
    
        }
    
    }
    
    • 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

    SecurityConfig

    package com.system.security;
    
    import com.system.common.utlis.jwt.JwtUtils;
    import com.system.common.utlis.result.Prefix;
    import com.system.common.utlis.result.ResData;
    import com.system.common.utlis.result.ResEnum;
    import com.system.system.dao.RoleDao;
    import com.system.system.entity.vo.AuthVO;
    import jakarta.annotation.Resource;
    import jakarta.servlet.http.HttpServletRequest;
    import jakarta.servlet.http.HttpServletResponse;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.security.access.AccessDeniedException;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
    import org.springframework.security.config.http.SessionCreationPolicy;
    import org.springframework.security.core.Authentication;
    import org.springframework.security.core.AuthenticationException;
    import org.springframework.security.core.GrantedAuthority;
    import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
    import org.springframework.security.crypto.password.PasswordEncoder;
    import org.springframework.security.web.SecurityFilterChain;
    import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
    
    import java.io.IOException;
    import java.io.PrintWriter;
    import java.util.HashSet;
    import java.util.List;
    import java.util.Set;
    
    /**
     * @author kuaiting
     */
    @Configuration
    public class SecurityConfig {
    
        @Resource
        JwtUtils jwtUtils;
    
        @Resource
        private JwtFilter jwtFilter;
    
        @Resource
        private RoleDao roleDao;
    
    
    
        @Bean
        public PasswordEncoder passwordEncoder() {
            return new BCryptPasswordEncoder();
        }
    
        private final String[] paths = {
                "/druid/**", "/system/captcha/line",
                "/druid/login.html/**",
                "/system/login", "/js/**", "/*/*.json", "/*/*.yml",
                "/prims/**", "/type/**", "/system/file/**",
                "/diagram-viewer/**", "/images/**",
                "/api/login/**", "/api/file/**",
                "/css/**", "/*/*.ico", "/swagger-resources/**",
                "/swagger/**", "/swagger-ui/**",
                "/webjars/**", "/v3/**", "/v2/**", "/doc.html/**"
        };
    
        @Bean
        public SecurityFilterChain securityChain(HttpSecurity http) throws Exception {
    
            return http.authorizeHttpRequests(conf -> conf.requestMatchers(paths).permitAll()
                    .anyRequest().authenticated())
                    .formLogin(conf ->
                            conf.loginProcessingUrl("/system/login")
                            .usernameParameter("username")
                            .passwordParameter("password")
                            .successHandler(this::onAuthenticationSuccess)
                            .failureHandler(this::onAuthenticationFailure))
                    .exceptionHandling(conf ->
                             conf.authenticationEntryPoint(this::noLogin)
                            .accessDeniedHandler(this::noPermission))
                    .logout(conf ->
                             conf.logoutUrl("/system/logout")
                            .logoutSuccessHandler(this::onLogoutSuccess))
                    .csrf(AbstractHttpConfigurer::disable)
                    .sessionManagement(conf -> conf.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                    .addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class)
                    .build();
    
        }
    
        private void noPermission(HttpServletRequest request,
                                  HttpServletResponse response,
                                  AccessDeniedException e) throws IOException {
            response.setContentType("application/json;charset=UTF-8");
            response.setStatus(HttpServletResponse.SC_FORBIDDEN);
            PrintWriter writer = response.getWriter();
            jsonWriter(writer, ResEnum.FORBIDDEN.getCode(), ResEnum.FORBIDDEN.getMsg());
    
        }
    
        public void onAuthenticationSuccess(HttpServletRequest request,
                                            HttpServletResponse response,
                                            Authentication authentication) throws IOException {
            SecurityUser user = (SecurityUser) authentication.getPrincipal();
            Long uid = user.getId();
            Set<String> permissions = new HashSet<>();
            for (GrantedAuthority authority : user.getAuthorities()) {
                String auth = authority.getAuthority();
                permissions.add(auth);
            }
            String token = jwtUtils.createToken(user, uid, user.getUsername(),user.getPassword());
            AuthVO authVo = new AuthVO();
            authVo.setRole(roleDao.getUserRoles(uid));
            authVo.setPermission(permissions);
            authVo.setKey(Prefix.TOKEN_KEY);
            authVo.setToken(token);
            authVo.setExpire(jwtUtils.expireTime().getTime());
            response.setContentType("application/json;charset=UTF-8");
            response.setStatus(HttpServletResponse.SC_OK);
            PrintWriter writer = response.getWriter();
            jsonWriter(writer, ResEnum.SUCCESS.getCode(), ResEnum.SUCCESS.getMsg(), authVo);
        }
    
        public void onAuthenticationFailure(HttpServletRequest request,
                                            HttpServletResponse response,
                                            AuthenticationException exception) throws IOException {
            response.setContentType("application/json;charset=UTF-8");
            response.setStatus(HttpServletResponse.SC_OK);
            PrintWriter writer = response.getWriter();
            jsonWriter(writer, ResEnum.UNAUTHORIZED.getCode(), exception.getMessage());
        }
    
        public void noLogin(HttpServletRequest request, HttpServletResponse response,
                            AuthenticationException authException) throws IOException {
            response.setContentType("application/json;charset=UTF-8");
            response.setStatus(HttpServletResponse.SC_OK);
            PrintWriter writer = response.getWriter();
            jsonWriter(writer, ResEnum.UNAUTHORIZED.getCode(), ResEnum.UNAUTHORIZED.getMsg());
        }
    
    
        public void onLogoutSuccess(HttpServletRequest request,
                                    HttpServletResponse response,
                                    Authentication authentication) throws IOException {
            String authorization = request.getHeader("Authorization");
            response.setContentType("application/json;charset=UTF-8");
            response.setStatus(HttpServletResponse.SC_OK);
            PrintWriter writer = response.getWriter();
            // 登出时,删除redis中的token
            if (jwtUtils.invalidateToken(authorization)) {
                jsonWriter(writer, ResEnum.SUCCESS.getCode(), ResEnum.SUCCESS.getMsg());
            } else {
                jsonWriter(writer, ResEnum.FAIL.getCode(), ResEnum.FAIL.getMsg());
            }
    
    
        }
    
        private void jsonWriter(PrintWriter writer, Integer code, String message) {
            jsonWriter(writer, code, message, null);
        }
    
        private void jsonWriter(PrintWriter writer, Integer code, String message, Object data) {
            writer.write(ResData.asJson(code, message, data));
            writer.flush();
            writer.close();
        }
    
    
        private List<Long> getRoleIds(Long uid) {
            return roleDao.getRoleIds(uid);
        }
    
        private List<String> getUserRoles(Long uid) {
            return roleDao.getUserRoles(uid);
        }
    
        private Set<String> getPermissions(List<Long> roleIds) {
            Set<String> all = new HashSet<>();
            for (Long id : roleIds) {
                List<String> permissions = getUserPermissions(id);
                all.addAll(permissions);
            }
            return all;
        }
    
        private List<String> getUserPermissions(Long rid) {
            return roleDao.getPermissions(rid);
        }
    
    
    }
    
    
    • 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

    User

    package com.system.security;
    
    import lombok.Data;
    import lombok.NoArgsConstructor;
    import org.springframework.security.core.GrantedAuthority;
    import org.springframework.security.core.authority.SimpleGrantedAuthority;
    import org.springframework.security.core.userdetails.UserDetails;
    
    import java.io.Serializable;
    import java.util.Collection;
    import java.util.Set;
    import java.util.stream.Collectors;
    
    /**
     * @author kuaiting
     */
    @Data
    @NoArgsConstructor
    public class SecurityUser implements UserDetails, Serializable {
        private Long id;
        private String username;
        private String password;
        private Integer status;
        private Set<String> permissions;
        private  Collection<? extends GrantedAuthority> authorities;
    
    
        public SecurityUser( Long id,String username, String password, Integer status, Set<String> permissions) {
            this.id = id;
            this.username = username;
            this.password = password;
            this.status = status;
            this.permissions = permissions;
        }
    
    
        @Override
        public Collection<? extends GrantedAuthority> getAuthorities() {
            if (authorities != null) {
                return authorities;
            }
            //把permissions中字符串类型的权限信息转换成GrantedAuthority对象存入authorities中
            authorities = permissions.stream().
                    map(SimpleGrantedAuthority::new)
                    .collect(Collectors.toList());
            return authorities;
        }
    
        @Override
        public String getPassword() {
            return password;
        }
    
        @Override
        public String getUsername() {
            return username;
        }
    
        @Override
        public boolean isAccountNonExpired() {
            return true;
        }
    
        @Override
        public boolean isAccountNonLocked() {
            return true;
        }
    
        @Override
        public boolean isCredentialsNonExpired() {
            return true;
        }
    
        @Override
        public boolean isEnabled() {
            return status == 0;
        }
    }
    
    
    • 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

    JwtUtils

    package com.system.common.utlis.jwt;
    
    import com.auth0.jwt.JWT;
    import com.auth0.jwt.algorithms.Algorithm;
    import com.auth0.jwt.exceptions.JWTVerificationException;
    import com.auth0.jwt.interfaces.Claim;
    import com.auth0.jwt.interfaces.DecodedJWT;
    import com.system.common.utlis.redis.RedisService;
    import com.system.common.utlis.result.Prefix;
    import com.system.security.SecurityUser;
    import jakarta.annotation.Resource;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.security.core.GrantedAuthority;
    import org.springframework.security.core.authority.SimpleGrantedAuthority;
    import org.springframework.security.core.userdetails.UserDetails;
    import org.springframework.stereotype.Component;
    
    import java.util.*;
    import java.util.stream.Collectors;
    
    @Component
    public class JwtUtils {
        @Value("${jwt.secret}")
        private String secret;
        @Value("${jwt.expire}")
        private Integer expire;
    
        @Resource
        private RedisService redisService;
    
        public Boolean invalidateToken(String haeadToken) {
            String token = convertToken(haeadToken);
            if (token == null) {
                return false;
            }
            try {
                DecodedJWT jwt = JWT.require(Algorithm.HMAC256(secret)).build().verify(token);
                return deleteToken(jwt.getId(), jwt.getExpiresAt());
            } catch (JWTVerificationException e) {
                return null;
            }
        }
    
        // 删除redis 中的Token
        private Boolean deleteToken(String id, Date date) {
            if (isInvalidToken(id)) {
                return false;
            }
            Date now = new Date();
            long expire = Math.max(date.getTime() - now.getTime(), 0);
            redisService.set(Prefix.JWT_BLACK_LIST + id, id, expire);
            return true;
        }
    
        // 验证 redis 中 token 是否存在
        private Boolean isInvalidToken(String id) {
            return Boolean.TRUE.equals(redisService.hasKey(Prefix.JWT_BLACK_LIST + id));
        }
    
    
        public String createToken(UserDetails details, Long id, String username,String password) {
            Algorithm algorithm = Algorithm.HMAC256(secret);
            redisService.set(Prefix.JWT_BLACK_LIST + id, id, expire);
            return JWT.create().withJWTId(String.valueOf(id))
                    .withClaim("id", id)
                    .withClaim("username", username)
                    .withClaim("password", password)
                    .withClaim("authorities", getAuths(details))
                    .withExpiresAt(expireTime())
                    .sign(algorithm);
        }
    
        public String getAuths(UserDetails details) {
            return details.getAuthorities().stream().map(GrantedAuthority::getAuthority).
                    collect(Collectors.joining(","));
        }
    
        // 获取过期时间
        public Date expireTime() {
            // 过期时间
            Calendar instance = Calendar.getInstance();
            instance.add(Calendar.HOUR, expire * 24); // 默认7天
            return instance.getTime();
        }
    
        // 解析 JWT token
        public DecodedJWT resolveToken(String haeadToken) {
            String token = convertToken(haeadToken);
            if (token == null) {
                return null;
            }
            try {
                DecodedJWT jwt = JWT.require(Algorithm.HMAC256(secret)).build().verify(token);
                if (isInvalidToken(jwt.getId())) {
                    return null;
                }
                Date expires = jwt.getExpiresAt();
                return new Date().after(expires) ? null : jwt;
            } catch (JWTVerificationException e) {
                return null;
            }
        }
    
        // 解析 截取真正有用的token
        private String convertToken(String haeadToken) {
            if (haeadToken == null || !haeadToken.startsWith(Prefix.TOKEN_BEARER)) {
                return null;
            }
            return haeadToken.substring(7);
        }
    
        // 获取用户信息
        public SecurityUser getUserDetails(DecodedJWT jwt) {
            Map<String, Claim> claims = jwt.getClaims();
            String authorities = claims.get("authorities").asString();
            Set<SimpleGrantedAuthority> permissions = new HashSet<>();
            for (String auth : authorities.split(",")) {
                permissions.add(new SimpleGrantedAuthority(auth));
            }
            SecurityUser sysUser = new SecurityUser();
            sysUser.setId(jwt.getClaim("id").asLong());
            sysUser.setUsername(claims.get("username").toString());
            sysUser.setPassword(claims.get("password").toString());
            sysUser.setStatus(0);
            sysUser.setAuthorities(permissions);
            return sysUser;
        }
    
        // 获取用户ID
        public Long getUid(DecodedJWT jwt) {
            return jwt.getClaim("id").asLong();
        }
    }
    
    
    • 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
  • 相关阅读:
    java计算机毕业设计ssm社区养老服务管理系统iq0w7(附源码、数据库)
    【计算机网络】详解TCP协议(下) 滑动窗口 | 流量控制 | 拥塞控制
    HJ5 进制转换
    CleanMyMac X2023标准版解锁完整版本Mac电脑清理专家
    【云计算赛项】职业技能竞赛--容器开发部分例题Pig快速开发框架
    网络通信设备之网络路由技术详解
    骑士精神 ← IDA*
    linux文件IO
    ValueError: need at least one array to concatenate
    vmware 用不了声音,设置声音那个位置的输出是伪输出,输入也不能动
  • 原文地址:https://blog.csdn.net/qq_45834006/article/details/136403939