spring boot + spring security + jwt + React 不工作

Posted

技术标签:

【中文标题】spring boot + spring security + jwt + React 不工作【英文标题】:spring boot + spring security + jwt + React not working 【发布时间】:2021-07-28 12:56:46 【问题描述】:

我正在使用 Spring boot 2.4 + spring security + jwt + React 构建一个新项目。我们在 react 中创建了一个登录页面,并且 react 构建位于静态文件夹中。 请找到以下代码: 我的security.java

@Configuration
@EnableWebSecurity
public class WebSecurity extends WebSecurityConfigurerAdapter 

    @Autowired
    UserDetailServiceImpl userDetails;
    
    @Autowired
    JWTAuthenticationFilter jwtRequestFilter;

    @Autowired
    public void configAuthentication(AuthenticationManagerBuilder auth) throws Exception 
        auth.userDetailsService(userDetails);
    

    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception 
        httpSecurity.cors().and().csrf().disable().authorizeRequests()
                    .antMatchers("/login/**, /resources/**").permitAll()
                    .antMatchers("/authenticate/**").permitAll()
                    .anyRequest().authenticated()
                    .and().exceptionHandling()
                    .and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        httpSecurity.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);

    

    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception 
        return super.authenticationManagerBean();
    
    
     @Bean
        public PasswordEncoder getPasswordEncoder() 
            return NoOpPasswordEncoder.getInstance();
        

在 UserDetailServiceImpl 中,我们从 mysql db 中获取用户 loadUserByUsername(字符串用户名)

JWTAuthenticationFilter.java

@Component
public class JWTAuthenticationFilter extends OncePerRequestFilter 
@Autowired
private UserDetailServiceImpl userDetailService;

@Autowired
private JwtTokenUtil jwtUtil;

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

        final String authorizationHeader = request.getHeader("Authorization");

        String username = null;
        String jwt = null;

        if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) 
            jwt = authorizationHeader.substring(7);
            username = jwtUtil.extractUsername(jwt);
        

        if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) 

            UserDetails userDetails = this.userDetailService.loadUserByUsername(username);

            if (jwtUtil.validateToken(jwt, userDetails)) 

                UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
                        userDetails, null, userDetails.getAuthorities());
                usernamePasswordAuthenticationToken
                        .setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
            
        
        filterChain.doFilter(request, response);

    


JwtTokenUtil.java

@Component
public class JwtTokenUtil implements Serializable 
    
    private static final long serialVersionUID = -3301605591108950415L;

    static final String CLAIM_KEY_USERNAME = "sub";
    static final String CLAIM_KEY_AUDIENCE = "aud";
    static final String CLAIM_KEY_CREATED = "iat";

    static final String AUDIENCE_UNKNOWN = "unknown";
    static final String AUDIENCE_WEB = "web";
    static final String AUDIENCE_MOBILE = "mobile";
    static final String AUDIENCE_TABLET = "tablet";


    //@Value("$jwt.secret")
    private String secret="ThisIsASecret";

    //@Value("$jwt.expiration")
    private Long expiration=604800L;

    public String extractUsername(String token) 
        return extractClaim(token, Claims::getSubject);
    

    public Date extractExpiration(String token) 
        return extractClaim(token, Claims::getExpiration);
    

    public <T> T extractClaim(String token, Function<Claims, T> claimsResolver) 
        final Claims claims = extractAllClaims(token);
        return claimsResolver.apply(claims);
    
    private Claims extractAllClaims(String token) 
        return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
    

    private Boolean isTokenExpired(String token) 
        return extractExpiration(token).before(new Date());
    

    public String generateToken(UserDetails userDetails) 
        Map<String, Object> claims = new HashMap<>();
        return createToken(claims, userDetails.getUsername());
    

    private String createToken(Map<String, Object> claims, String subject) 

        return Jwts.builder().setClaims(claims).setSubject(subject).setIssuedAt(new Date(System.currentTimeMillis()))
                .setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 10))
                .signWith(SignatureAlgorithm.HS256, secret).compact();
    

    public Boolean validateToken(String token, UserDetails userDetails) 
        final String username = extractUsername(token);
        return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
    

我的控制器:

@RestController
public class LoginController 

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private JwtTokenUtil jwtTokenUtil;

    @Autowired
    private UserDetailServiceImpl userDetailsService;
    
    @GetMapping(value =  "/welcome" )
    public String welcome(HttpServletRequest request) throws Exception 
    
            return "login sucess";
        
    
    
    @PostMapping(value = "/authenticate")
    public ResponseEntity<?> createAuthenticationToken(@RequestBody AccountCredentials authenticationRequest) throws Exception 

        try 
            authenticationManager.authenticate(
                    new UsernamePasswordAuthenticationToken(authenticationRequest.getUsername(), authenticationRequest.getPassword())
            );
        
        catch (BadCredentialsException e) 
            throw new Exception("Incorrect username or password", e);
        


        final UserDetails userDetails = userDetailsService
                .loadUserByUsername(authenticationRequest.getUsername());

        final String jwt = jwtTokenUtil.generateToken(userDetails);

        return ResponseEntity.ok(new AuthenticationResponse(jwt));
    

/authenticate 是禁止的,经过邮递员测试。

<?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>
    <packaging>war</packaging>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.4.4</version>
        <relativePath /> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.cmp</groupId>
    <artifactId>project</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>project</name>
    <description>Project boot react</description>
    <properties>
        <java.version>11</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <!-- <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency> -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
    <!--    <dependency>
            <groupId>org.liquibase</groupId>
            <artifactId>liquibase-core</artifactId>
        </dependency> -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-mail</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-validation</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.mariadb.jdbc</groupId>
            <artifactId>mariadb-java-client</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>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.1</version>
        </dependency>
        <dependency>
    <groupId>javax.xml.bind</groupId>
    <artifactId>jaxb-api</artifactId>
    <version>2.3.0</version>
</dependency>
        
    </dependencies>

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

</project>

我们正在寻找登录页面出现并生成 jwt + 通过所有安全性。它说禁止来自邮递员的 /authenticate api 并且 /login 未找到(登录页面是在反应中创建的,并且构建在资源/静态文件夹中)。注意:我们在单服务器上运行,即tomcat 9 + java 11。控制台没有异常。

【问题讨论】:

请提出任何建议,请让我知道您的想法? 【参考方案1】:

我想通了。代码改动很少。

类 JWTAuthenticationFilter -> 改变 UsernamePasswordAuthenticationToken 参数 userDetails 到 userDetails.getUserName();

class WebSecurity -> 添加了 passwordEncoder(getPasswordEncoder());

如果有人需要帮助来实施它,请发表评论。谢谢

【讨论】:

以上是关于spring boot + spring security + jwt + React 不工作的主要内容,如果未能解决你的问题,请参考以下文章

UnsatisfiedDependencyException:创建名为“securityConfig”的 bean 时出错

SpringSecurity基于数据库认证

spring security 1

Spring securiuty 过滤器

Spring Boot 学习例子

Spring Boot 2Spring Boot CLI