集成spring security


一 引入依赖

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>    


二 新建配置类


@Configuration
@EnableMethodSecurity
@EnableWebSecurity
public class SecurityCfg {
@Value("${server.cors}")
private Boolean isCors;
@Autowired
private JwtFilter jwtFilter;


@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {

    httpSecurity
            .csrf(httpSecurityCsrfConfigurer -&gt; httpSecurityCsrfConfigurer.disable())
            .formLogin(httpSecurityFormLoginConfigurer -&gt; httpSecurityFormLoginConfigurer.disable())
            .httpBasic(httpSecurityHttpBasicConfigurer -&gt; httpSecurityHttpBasicConfigurer.disable())
            .logout(httpSecurityLogoutConfigurer -&gt; httpSecurityLogoutConfigurer.disable());

    if (isCors!=null && isCors){
        httpSecurity.cors(configurer -&gt; configurer.configurationSource(request -&gt; {
            CorsConfiguration corsConfiguration = new CorsConfiguration();
            corsConfiguration.addAllowedOrigin("*");
            corsConfiguration.addAllowedMethod("*");
            corsConfiguration.addAllowedHeader("*");
            return corsConfiguration;
        }));
    }

    httpSecurity.addFilterAt(jwtFilter, UsernamePasswordAuthenticationFilter.class);
    httpSecurity.addFilterBefore(new ExceptionFilter(),JwtFilter.class);
    //httpSecurity.addFilterAt(anonymousFilter, AnonymousAuthenticationFilter.class);
    
    httpSecurity.sessionManagement(httpSecuritySessionManagementConfigurer -&gt; 
            httpSecuritySessionManagementConfigurer.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
            
    httpSecurity.authorizeHttpRequests(auth-&gt; auth
            .requestMatchers("/swagger-ui/**").permitAll()
            .requestMatchers("/css/**","/js/**","/favicon.ico",
                    "/robots.txt","/manifest.json","/icon.png","/avatar.png","/sw.js"
            ).permitAll() //静态资源
            //访问都需要VIEW权限
            .anyRequest().hasAuthority("VIEW")
    );

    httpSecurity.exceptionHandling(configurer -&gt; {
        configurer.authenticationEntryPoint((request, response, accessDeniedException) -&gt; {
            throw new BadCredentialsException("未认证");
        });
        configurer.accessDeniedHandler((request, response, accessDeniedException) -&gt; {
            throw new AuthenticationException("未授权");
        });
    });
    return httpSecurity.build();
}

}


三 实现 UserDetailsService 接口

org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration

spring boot的 org.springframework.boot.autoconfigure.AutoConfiguration.imports 里引入该自动配置类. 当没有 ConditionalOnMissingBean 指定的这几个类时,会自动配置一个 InMemoryUserDetailsManager.所以只要实现一个 UserDetailsService 类就可以了.

@AutoConfiguration @ConditionalOnClass(AuthenticationManager.class) @Conditional(MissingAlternativeOrUserPropertiesConfigured.class) @ConditionalOnBean(ObjectPostProcessor.class) @ConditionalOnMissingBean(value = { AuthenticationManager.class, AuthenticationProvider.class, UserDetailsService.class, AuthenticationManagerResolver.class }, type = "org.springframework.security.oauth2.jwt.JwtDecoder") @ConditionalOnWebApplication(type = Type.SERVLET) public class UserDetailsServiceAutoConfiguration {

@Bean
public InMemoryUserDetailsManager inMemoryUserDetailsManager(SecurityProperties properties,
		ObjectProvider&lt;PasswordEncoder&gt; passwordEncoder) {
	SecurityProperties.User user = properties.getUser();
	List&lt;String&gt; roles = user.getRoles();
	return new InMemoryUserDetailsManager(User.withUsername(user.getName())
		.password(getOrDeducePassword(user, passwordEncoder.getIfAvailable()))
		.roles(StringUtils.toStringArray(roles))
		.build());
}

}

@Service
public class UserService implements UserDetailsService {

@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    return null;
}

}


四 使用jwt

1. 引入依赖

        <dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.19.2</version>
</dependency>

2.


/**

  • 从请求头获取token并装载用户 */ @Component public class JwtFilter extends OncePerRequestFilter { @Autowired private UserService userService;

    @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {

     //获取访问ip
     String fuck = request.getHeader("X-Forwarded-For");
     System.out.println("请求ip:"+fuck);
    
     Enumeration&lt;String&gt; headers = request.getHeaderNames();
     if (headers!=null){
         String tmp = "";
         while (headers.hasMoreElements()){
             tmp = tmp+" "+headers.nextElement();
         }
         System.out.println("gan:"+tmp);
     }
    
     //从header获取token
     String token = request.getHeader("Authentication");
    
     //header中没有token,则从cookie获取token
     if (token==null || token.isEmpty()){
         Cookie[] cookies = request.getCookies();
         if (cookies==null || cookies.length==0){
             filterChain.doFilter(request,response);
             return;
         }
         for (Cookie cookie:cookies){
             if (cookie.getName().equals("token")){
                 token = cookie.getValue();
                 break;
             }
         }
         //cookie中也没有token,则交给下一个过滤器
         if (token==null || token.equals("")){
             filterChain.doFilter(request,response);
             return;
         }
     }
     DecodedJWT decodedJWT = null;
     try {
         decodedJWT = JWT.decode(token);
     }catch (Exception e){
         return;
     }
     List&lt;String&gt; audience = decodedJWT.getAudience();
     String username = audience.getFirst();
    
     //用户密码作为加密盐
     if (username == null){
         throw new BadCredentialsException("认证信息缺失");
     }
    
     UserInfo userInfo = userService.getUserByUsername(username);
     if (userInfo == null){
         throw new BadCredentialsException("用户不存在");
     }
     JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(userInfo.getPassword())).build();
     try {
         jwtVerifier.verify(token);
     }catch (SignatureVerificationException e){
         e.printStackTrace();
         response.sendError(401);
         return;
     }catch (TokenExpiredException e){
         response.setStatus(401);
         return;
     }
    
     Set&lt;GrantedAuthority&gt; authorities = new HashSet&lt;&gt;();
     for (UserRole role:userInfo.getRoles()){
         SimpleGrantedAuthority simpleGrantedAuthority = new SimpleGrantedAuthority(role.getRole());
         authorities.add(simpleGrantedAuthority);
     }
     UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(
             username,
             userInfo.getPassword(),
             authorities
     );
     authenticationToken.setDetails(userInfo);
     SecurityContextHolder.getContext().setAuthentication(authenticationToken);
     filterChain.doFilter(request,response);
    

    } }



异常处理

spring boot @RestControllerAdvice 定义的全局异常捕获,只能捕获 servlet(Controller) 后的异常.

所以,在Controller上定义的hasAuthority,如果未登陆或者没有权限,可以交给全局异常处理程序处理.

Spring Security配置文件里,JwtFilter中的异常,最好直接抛出,新建一个ExceptionFilter来处理.


public class ExceptionFilter extends OncePerRequestFilter {

@Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { try { filterChain.doFilter(request,response); }catch (Exception e){ if (e instanceof DisabledException){ //已被封禁 请到 /block 查看封禁原因,如继续有疑问请联系客服. } else if (e instanceof BadCredentialsException){ //未登录 需要登录后再继续 response.setContentType("application/json;charset=UTF-8"); response.setStatus(401); response.getWriter().write(""); } else if (e instanceof AuthenticationException){ //没有权限 没有权限进行该操作 response.setContentType("application/json;charset=UTF-8"); response.setStatus(403); PrintWriter printWriter = response.getWriter(); printWriter.write(""); printWriter.flush(); } else { e.printStackTrace(); } } } }




@RestControllerAdvice
public class ExceptionHandle {

//无权限 @ExceptionHandler(AccessDeniedException.class) public void denied(HttpServletRequest request, HttpServletResponse response){ ResMap<String> resMap = ResMap.fail("没有权限进行该操作"); resMap.setCode(403); response.setStatus(403); response.setContentType("application/json;charset=UTF-8"); try { String tem = new ObjectMapper().writeValueAsString(resMap); response.getWriter().write(tem); }catch (Exception ignore){} }

//未登录 @ExceptionHandler(AuthenticationException.class) public void authenticationException(HttpServletResponse response){ //未登录 ResMap<String> resMap = ResMap.fail("需要登录后再继续"); resMap.setCode(401); response.setStatus(401); response.setContentType("application/json;charset=UTF-8"); try { String tem = new ObjectMapper().writeValueAsString(resMap); response.getWriter().write(tem); }catch (Exception ignore){} }

//无Square权限 @ExceptionHandler(SquareDisableException.class) public void squareDisabled(Exception e,HttpServletResponse response){ ResMap<String> resMap = ResMap.fail("square功能未开启"); resMap.setCode(315); response.setStatus(315); response.setContentType("application/json;charset=UTF-8"); try { String tem = new ObjectMapper().writeValueAsString(resMap); response.getWriter().write(tem); }catch (Exception ignore){} }

@ExceptionHandler(Exception.class) public void fuck(Exception e,HttpServletResponse response){ e.printStackTrace(); // System.out.println("fuck2 "); response.setStatus(400); } }