当前位置:   article > 正文

SpringBoot3 整合SpringSecurity_springboot3整合spring security

springboot3整合spring security

Maven

  <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.2.2</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
     <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </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
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/2023面试高手/article/detail/492752
推荐阅读
相关标签
  

闽ICP备14008679号