首页 > 解决方案 > 将 HTTP 替换为 HTTPS 后授权不起作用

问题描述

我用 spring-security 和 jwt 创建了一个应用程序。它运作良好。但这在我用 HTTPS 替换 HTTP 后不起作用。我已成功登录,但如果我使用任何 POST 请求到安全页面,则会收到 403 错误。其他请求(例如 GET)可以正常工作。

安全配置.java

@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

public static final String USER_ROLE = "USER";
public static final String ADMIN_ROLE = "ADMIN";

@Autowired
private DataSource dataSource;

@Bean
CorsConfigurationSource corsConfigurationSource() {
    CorsConfiguration config = new CorsConfiguration();
    config.setAllowCredentials(true);
    config.setAllowedOrigins(Arrays.asList("https://localhost:8888"));
    config.addAllowedHeader("*");
    config.addAllowedMethod("*");
    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    source.registerCorsConfiguration("/**", config);
    return source;
}


@Override
public void configure(HttpSecurity httpSecurity) throws Exception {
    httpSecurity
            .requiresChannel()
            .anyRequest()
            .requiresSecure();
    httpSecurity.cors();
    httpSecurity.csrf()
            .ignoringAntMatchers("/login")
            .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
    httpSecurity.requiresChannel().and().authorizeRequests()
            .antMatchers("/recipe/add/**", "/comment/getModerate/**", "/recipe/changeRecipe/**")
            .hasAnyRole(USER_ROLE, ADMIN_ROLE)
            .antMatchers("/**")
            .permitAll()
            .and()
            .addFilterBefore(new JWTLoginFilter("/login", authenticationManager()), UsernamePasswordAuthenticationFilter.class)
            .addFilterBefore(new JWTAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
    httpSecurity
            .logout()
            .logoutSuccessHandler(logoutHandler())
            .deleteCookies("JSESSIONID", "COOKIE-BEARER")
            .invalidateHttpSession(true)
            .permitAll();
}

@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
    auth.jdbcAuthentication()
            .dataSource(dataSource)
            .passwordEncoder(passwordEncoder())
            .usersByUsernameQuery("select username,password,enabled from users where username = ?")
            .authoritiesByUsernameQuery("select rs.username, r.roles from users rs inner join user_role r on rs.id = r.role_id where rs.username = ?");
}

@Bean
public PasswordEncoder passwordEncoder() {
    PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
    return encoder;
}

@Bean
public CustomLogoutSuccessHandler logoutHandler() {
    return new CustomLogoutSuccessHandler();
}

}

JWTAuthenticationFilter.java

public class JWTAuthenticationFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(final HttpServletRequest request, final HttpServletResponse response, final FilterChain filterChain) throws ServletException,
        IOException {
    try {
        Authentication authentication = TokenAuthenticationHelper.getAuthentication(request);
        SecurityContextHolder.getContext().setAuthentication(authentication);
        filterChain.doFilter(request, response);
    } catch (ExpiredJwtException | UnsupportedJwtException | MalformedJwtException |
            SignatureException | IllegalArgumentException e) {
        response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Token expired");
    }
}
}

JWTLoginFilter.java

public class JWTLoginFilter extends AbstractAuthenticationProcessingFilter {

public JWTLoginFilter(String url, AuthenticationManager authManager) {
    super(new AntPathRequestMatcher(url));
    setAuthenticationManager(authManager);
}

@Override
public Authentication attemptAuthentication(HttpServletRequest req, HttpServletResponse res) throws IOException {

    AccountCredentials creds = new ObjectMapper().readValue(req.getInputStream(), AccountCredentials.class);
    return getAuthenticationManager().authenticate(
            new UsernamePasswordAuthenticationToken(
                    creds.getUsername(),
                    creds.getPassword(),
                    creds.getAuthorities()
            )
    );
}

@Override
protected void successfulAuthentication(HttpServletRequest req, HttpServletResponse res, FilterChain chain, Authentication auth) {
    TokenAuthenticationHelper.addAuthentication(res, auth);
}

static class AccountCredentials {
    private String username;
    private String password;
    private Collection<GrantedAuthority> authorities;

    String getUsername() {
        return username;
    }

    void setUsername(String username) {
        this.username = username;
    }

    String getPassword() {
        return password;
    }

    void setPassword(String password) {
        this.password = password;
    }

    Collection<GrantedAuthority> getAuthorities() {
        return authorities;
    }

    void setAuthorities(Collection<GrantedAuthority> authorities) {
        this.authorities = authorities;
    }
}
}

TokenAuthenticationHelper.java

class TokenAuthenticationHelper {
    private static final long EXPIRATION_TIME = 1000 * 60 * 60 * 3; // 3 hours
    private static final String SECRET = "ThisIsASecret";
    private static final String COOKIE_BEARER = "COOKIE-BEARER";
private TokenAuthenticationHelper() {
    throw new IllegalStateException("Utility class");
}

static void addAuthentication(HttpServletResponse res, Authentication auth) {

    String authorities = auth.getAuthorities().stream()
            .map(GrantedAuthority::getAuthority)
            .collect(Collectors.joining(","));

    String jwt = Jwts.builder()
            .setSubject(auth.getName())
            .claim("authorities", authorities)
            .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
            .signWith(SignatureAlgorithm.HS512, SECRET)
            .compact();
    Cookie cookie = new Cookie(COOKIE_BEARER, jwt);
    cookie.setHttpOnly(true);
    cookie.setPath("/");
    res.addCookie(cookie);
}

static Authentication getAuthentication(HttpServletRequest request) {
    System.out.println(request);
    Cookie cookie = WebUtils.getCookie(request, COOKIE_BEARER);
    String token = cookie != null ? cookie.getValue() : null;
    if (token != null) {
        Claims claims = Jwts.parser()
                .setSigningKey(SECRET)
                .parseClaimsJws(token)
                .getBody();

        Collection<? extends GrantedAuthority> authorities =
                Arrays.stream(claims.get("authorities").toString().split(","))
                        .map(SimpleGrantedAuthority::new)
                        .collect(Collectors.toList());

        String userName = claims.getSubject();
        return userName != null ? new UsernamePasswordAuthenticationToken(userName, null, authorities) : null;
    }
    return null;
}
}

服务器配置.java

@Configuration
public class ServerConfig {

    @Bean
    public ServletWebServerFactory servletContainer() {
        TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory() {
            @Override
            protected void postProcessContext(Context context) {
                SecurityConstraint securityConstraint = new SecurityConstraint();
                securityConstraint.setUserConstraint("ROLE_ADMIN");
                SecurityCollection collection = new SecurityCollection();
                collection.addPattern("/*");
                securityConstraint.addCollection(collection);
                context.addConstraint(securityConstraint);
            }
        };
        tomcat.addAdditionalTomcatConnectors(getHttpConnector());
        return tomcat;
    }

    private Connector getHttpConnector() {
        Connector connector = new Connector(TomcatServletWebServerFactory.DEFAULT_PROTOCOL);
        connector.setScheme("http");
        connector.setPort(8080);
        connector.setSecure(false);
        connector.setRedirectPort(8081);
        return connector;
    }
}

应用程序属性

server.port=8081    
#server.ssl.key-store-type=PKCS12
server.ssl.key-store=classpath:keystore.p12
server.ssl.key-store-password=123456
server.ssl.key-alias=back

我认为问题出在 csrf 令牌上。我不能csrf().disable()。你能帮我理解什么是错的吗?

标签: javaspring-bootspring-securityjwtcsrf

解决方案


推荐阅读