maven clean install 和 mvn spring-boot:run 在 POST 请求上给我 401(未经授权)错误

问题描述 投票:0回答:1

当我使用“mvn clean package”和“mvn spring-boot:run”运行我的java spring boot项目时,我收到 POST http://localhost:8080/api/v1/register 401(未经授权)错误。当我使用 control + R 或运行按钮在 IntelliJ IDEA 中运行 Java 21 后端项目时,我可以将端口 3000 上的 React 前端发送到端口 8080 上的 java 后端,并将用户数据保存到端口上的 mysql 数据库3306.

我一直在谷歌搜索、阅读、尝试并努力寻找一个可以通过构建以及当 mysql 数据库位于 Docker 中时工作的可行解决方案。使用 Java 21 和 Spring Boot 3.2.4 如果能提供一些帮助,我们将不胜感激。

网络配置

package com.cinema.backend.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

import java.util.Arrays;

@Configuration
public class WebConfig {

    private static final long MAX_AGE = 3600L;

    @Bean
    public CorsFilter corsFilter() {
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowCredentials(true);
        config.addAllowedOrigin("http://localhost:3000");
        config.setAllowedHeaders(Arrays.asList(
                "Authorization",
                "Cache-Control",
                "Content-Type"));
        config.setAllowedMethods(Arrays.asList(
                HttpMethod.GET.name(),
                HttpMethod.POST.name(),
                HttpMethod.PUT.name(),
                HttpMethod.DELETE.name(),
                HttpMethod.OPTIONS.name()));
        config.setMaxAge(MAX_AGE);

        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", config);

        return new CorsFilter(source);
    }

}

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.2.4</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.cinema</groupId>
    <artifactId>backend</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>backend</name>
    <description>Backend Application for Cinema</description>
    <properties>
        <java.version>21</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

控制器

package com.cinema.backend.controller;

import com.cinema.backend.models.User;
import com.cinema.backend.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class UserController {

    @Autowired
    private UserRepository userRepository;

    @PostMapping("/api/v1/register")
    User newUser(@RequestBody User newUser){
        return userRepository.save(newUser);
    }

}

java mysql spring-boot maven spring-mvc
1个回答
0
投票

根据您的 Spring Security 依赖项,您的请求中必须有一个授权标头。

如果您不想保护您的应用程序,您可以删除此依赖项。

如果你想保护你的应用程序,你必须像下面这样配置网络安全

package com.dpco.business.security;

import com.dpco.business.exception.CustomException;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Component
@Configuration
@EnableWebSecurity
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {
    @Override
    public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {

//       httpServletResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, "UNAUTHORIZED");
        throw new CustomException(e.getMessage() , HttpStatus.INTERNAL_SERVER_ERROR);
    }
}
package com.dpco.business.security;

import com.dpco.business.dto.LoginDto;
import com.dpco.business.exception.CustomException;
import com.dpco.logger.Logger4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

import java.util.List;

@Component
public class JwtAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {

    @Autowired
    private JwtValidator validator;

    @Autowired
    private Logger4j logger4j;

    @Override
    protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken) throws AuthenticationException {

    }

    @Override
    protected UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken) throws AuthenticationException {

        try {
            JwtAuthenticationToken jwtAuthenticationToken = (JwtAuthenticationToken) usernamePasswordAuthenticationToken;
            String token = jwtAuthenticationToken.getToken();

            LoginDto jwtUser = validator.validate(token);

            List<GrantedAuthority> grantedAuthorities = AuthorityUtils
                    .commaSeparatedStringToAuthorityList(jwtUser.getRole());
            return new JwtUserDetails(jwtUser.getUsername(), jwtUser.getId(),
                    token,
                    grantedAuthorities);
        }catch (Exception ex){
            logger4j.getLogger(ex);
            throw new CustomException(ex.getMessage() , HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }

    @Override
    public boolean supports(Class<?> aClass) {
        try {
            return (JwtAuthenticationToken.class.isAssignableFrom(aClass));
        }catch (Exception ex){
            logger4j.getLogger(ex);
            throw new CustomException(ex.getMessage() , HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }
}


package com.dpco.business.security;

import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;

public class JwtAuthenticationToken extends UsernamePasswordAuthenticationToken{

    private String token;

    public JwtAuthenticationToken(String token) {
        super(null, null);
        this.token = token;
    }

    public String getToken() {
        return token;
    }

    public void setToken(String token) {
        this.token = token;
    }

    @Override
    public Object getCredentials() {
        return null;
    }

    @Override
    public Object getPrincipal() {
        return null;
    }
}


package com.dpco.business.security;

import com.dpco.business.exception.CustomException;
import com.dpco.logger.Logger4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class JwtAuthenticationTokenFilter extends AbstractAuthenticationProcessingFilter {

    @Autowired
    private Logger4j logger4j;

    public JwtAuthenticationTokenFilter() {
        super("/member/**");
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws AuthenticationException, IOException, ServletException {
        try {
            String content = httpServletRequest.getHeader("Content-Type");
            System.out.println("--------------------------------");
            System.out.println(content);
            System.out.println("---------------------------------");
            String header = httpServletRequest.getHeader("Authorization");
            if (header == null || !header.startsWith("Bearer ")) {
                throw new CustomException("JWT Token is missing or is not valid", HttpStatus.FORBIDDEN);
            }
            String authenticationToken = header.substring(6);
            JwtAuthenticationToken token = new JwtAuthenticationToken(authenticationToken);
            return getAuthenticationManager().authenticate(token);
        }catch (Exception ex){
            logger4j.getLogger(ex);
            throw new CustomException(ex.getMessage() , HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }


    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
        try {
            super.successfulAuthentication(request, response, chain, authResult);
            chain.doFilter(request, response);
        }catch (Exception ex){

            throw new CustomException(ex.getMessage() , HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }
}


package com.dpco.business.security;

import com.dpco.business.dto.LoginDto;
import com.dpco.business.exception.CustomException;

import com.dpco.logger.Logger4j;
import com.dpco.business.service.MemberService;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;

import java.util.Date;

@Component
public class JwtGenerator {

    @Autowired
    private MemberService memberService;

    @Autowired
    private Logger4j logger4j;

    @Value("${security.jwt.token.expire-length}") // 1 hour
    private long validityInMilliseconds;

    public String generate(LoginDto loginDto){
        try {
            if (memberService.findByUsernameAndPassword(loginDto.getUsername() , loginDto.getPassword()) != null) {
                Claims claims = Jwts.claims()
                        .setSubject(loginDto.getUsername());
                claims.put("userId", String.valueOf(loginDto.getId()));
                claims.put("role", loginDto.getRole());
                Date validity = new Date(new Date().getTime() + validityInMilliseconds);
                return Jwts.builder()
                        .setClaims(claims)
                        .signWith(SignatureAlgorithm.HS512, "dpco")
                        .setExpiration(validity)
                        .compact();
            } else {
//                throw new CustomException("there is no member with this username and password so it is forbidden", HttpStatus.FORBIDDEN);
                return null;
            }
        }catch (Exception ex){
            logger4j.getLogger(ex);
            throw new CustomException(ex.getMessage() , HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }
}


package com.dpco.business.security;


import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class JwtSuccessHandler implements AuthenticationSuccessHandler{
    @Override
    public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
        System.out.println("Successfully Authentication");
    }
}

package com.dpco.business.security;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import java.util.Collection;
import java.util.List;

public class JwtUserDetails implements UserDetails {

    private String userName;
    private String token;
    private Long id;
    private Collection<? extends GrantedAuthority> authorities;


    public JwtUserDetails(String userName, long id, String token, List<GrantedAuthority> grantedAuthorities) {

        this.userName = userName;
        this.id = id;
        this.token= token;
        this.authorities = grantedAuthorities;
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return authorities;
    }

    @Override
    public String getPassword() {
        return null;
    }

    @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 true;
    }


    public String getUserName() {
        return userName;
    }

    public String getToken() {
        return token;
    }


    public Long getId() {
        return id;
    }

}
package com.dpco.business.security;

import com.dpco.business.dto.LoginDto;
import com.dpco.business.exception.CustomException;
import com.dpco.logger.Logger4j;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;

@Component
public class JwtValidator {


    private String secret = "dpco";

    @Autowired
    private Logger4j logger4j;

    public LoginDto validate(String token) {

        LoginDto jwtUser = null;
        try {
            Claims body = Jwts.parser()
                    .setSigningKey(secret)
                    .parseClaimsJws(token)
                    .getBody();

            jwtUser = new LoginDto();

            jwtUser.setUsername(body.getSubject());
            jwtUser.setId(Long.parseLong((String) body.get("userId")));
            jwtUser.setRole((String) body.get("role"));
        }
        catch (Exception e) {
            logger4j.getLogger(e);
            throw new CustomException("this jwt is not valid" , HttpStatus.FORBIDDEN);
        }

        return jwtUser;
    }
}

然后将此配置添加到您的配置包中

package com.dpco.business.config;

import com.dpco.business.exception.CustomException;
import com.dpco.business.security.JwtAuthenticationEntryPoint;
import com.dpco.business.security.JwtAuthenticationProvider;
import com.dpco.business.security.JwtAuthenticationTokenFilter;
import com.dpco.business.security.JwtSuccessHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import java.util.Collections;

@EnableGlobalMethodSecurity(prePostEnabled = true)
@EnableWebSecurity
@Configuration
public class JwtSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private JwtAuthenticationProvider authenticationProvider;
    @Autowired
    private JwtAuthenticationEntryPoint entryPoint;

    @Bean
    public AuthenticationManager authenticationManager() {
        try {
            return new ProviderManager(Collections.singletonList(authenticationProvider));
        } catch (CustomException ex) {
            throw new CustomException(ex.getMessage(), ex.getStatus());
        }
    }

    @Bean
    public JwtAuthenticationTokenFilter authenticationTokenFilter() {
        try {
            JwtAuthenticationTokenFilter filter = new JwtAuthenticationTokenFilter();
            filter.setAuthenticationManager(authenticationManager());
            filter.setAuthenticationSuccessHandler(new JwtSuccessHandler());
            return filter;
        } catch (CustomException ex) {
            throw new CustomException(ex.getMessage(), ex.getStatus());
        }
    }


    @Override
    protected void configure(HttpSecurity http) throws Exception {
        try {
            http.csrf().disable()
                    .authorizeRequests().antMatchers("**/member/**").authenticated()
                    .and().authorizeRequests().antMatchers("/v2/api-docs").permitAll()
                    .and()
                    .exceptionHandling().authenticationEntryPoint(entryPoint)
                    .and()
                    .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);

            http.addFilterBefore(authenticationTokenFilter(), UsernamePasswordAuthenticationFilter.class);
            http.headers().cacheControl();
        } catch (CustomException ex) {
            throw new CustomException(ex.getMessage(), ex.getStatus());
        }

    }
}

这是您用于获取令牌的登录控制器


package com.dpco.controller;

import com.dpco.business.dto.LoginDto;
import com.dpco.business.entity.Member;
import com.dpco.business.exception.CustomException;
import com.dpco.business.exception.ResultBody;
import com.dpco.business.security.JwtGenerator;
import com.dpco.business.service.MemberService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/")
@CrossOrigin("*")
public class LoginController {


    @Autowired
    private JwtGenerator jwtGenerator;
    @Autowired
    private MemberService memberService;


    @RequestMapping(path = "/login", method = RequestMethod.POST)
    public ResultBody generate(@RequestBody LoginDto loginDto) throws Exception {
        try {
            String token = jwtGenerator.generate(loginDto);
            System.out.println("--------------------------------------------------");
            System.out.println(token);
            System.out.println("----------------------------------------------------");
            return new ResultBody(token, HttpStatus.OK.value());
        } catch (CustomException ex) {
            throw new CustomException("some thing wrong in login", ex.getStatus());
        }
    }
}

package com.dpco.business.dto;

public class LoginDto {

    private String username;
    private String password;
    private long id;
    private String role;


    public LoginDto(String username, String password) {
        this.username = username;
        this.password = password;
    }

    public LoginDto() {
    }

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

    public void setId(long id) {
        this.id = id;
    }

    public void setRole(String role) {
        this.role = role;
    }

    public String getUsername() {
        return username;
    }

    public String getPassword() {
        return password;
    }

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

    public long getId() {
        return id;
    }

    public String getRole() {
        return role;
    }
}

之后,从该 api 获得的令牌必须添加到每个授权标头中

© www.soinside.com 2019 - 2024. All rights reserved.