当我点击“ / login”时,我想重新放置JWT。但是我缺少一些东西,无法解决。

以下是我的代码:

SecurytiApplication.java

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;

@EnableJpaRepositories
@ComponentScan(basePackages = "com.example.securyti")
@EntityScan(basePackages = "com.example.securyti")
@SpringBootApplication
public class SecurytiApplication {

    public static void main(String[] args) {
        SpringApplication.run(SecurytiApplication.class, args);

    }

}


SecurityConfig.java

package com.example.securyti.config;

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.config.annotation.authentication.builders.AuthenticationManagerBuilder;
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.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

import com.example.securyti.security.UserAccountService;
import com.example.securyti.security.jwt.JwtAuthenticationFilter;
import com.example.securyti.security.jwt.JwtAuthorizationFilter;
import com.example.securyti.security.jwt.JwtTokenService;

@EnableWebSecurity
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter{

    protected JwtAuthenticationFilter jwtAuthenticationFilter;
    protected JwtAuthorizationFilter JwtAuthorizationFilter;

    @Autowired
    protected UserAccountService userAccountService;

    @Autowired
    protected JwtTokenService jwtTokenService;

    @Autowired
    protected ConfigurationService configService;

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        final AuthenticationManager authenticationManager = authenticationManager();
        jwtAuthenticationFilter = new JwtAuthenticationFilter(authenticationManager,
            jwtTokenService, (BCryptPasswordEncoder) passwordEncoder(), userAccountService);

        JwtAuthorizationFilter = new JwtAuthorizationFilter(authenticationManager,
                configService, jwtTokenService);
        http
            .httpBasic().disable()
            .csrf().disable()
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            .and()
                .authorizeRequests()
                .antMatchers("/register")
                .permitAll()
                .anyRequest().authenticated().and().addFilter(jwtAuthenticationFilter).addFilter(JwtAuthorizationFilter);
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(final AuthenticationManagerBuilder auth)
          throws Exception {
        auth.userDetailsService(userAccountService).passwordEncoder(passwordEncoder());
    }
}


JwtAuthenticationFilter.java

package com.example.securyti.security.jwt;

import java.io.IOException;
import java.util.Collections;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.util.StringUtils;

import com.example.securyti.security.UserAccount;
import com.example.securyti.security.UserAccountService;

public class JwtAuthenticationFilter extends UsernamePasswordAuthenticationFilter {

    private static final Logger logger = LoggerFactory.getLogger(JwtAuthenticationFilter.class);

    private final AuthenticationManager authenticationManager;
      private final JwtTokenService jwtTokenService;
      private final BCryptPasswordEncoder passwordEncoder;
      private final UserAccountService userAccountService;

      public JwtAuthenticationFilter(
          final AuthenticationManager authenticationManager,
          final JwtTokenService jwtTokenService,
          final BCryptPasswordEncoder passwordEncoder,
          final UserAccountService userAccountService) {
        this.authenticationManager = authenticationManager;
        this.jwtTokenService = jwtTokenService;
        this.passwordEncoder = passwordEncoder;
        this.userAccountService = userAccountService;
      }

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

        String jwt = jwtTokenService.getTokenFromRequest(req);
        UserAccount userAccount = null;

        if (StringUtils.hasText(jwt) && jwtTokenService.validateToken(jwt)) {
            userAccount = (UserAccount) userAccountService.loadUserByUsername(jwtTokenService.getUsernameFromJWT(jwt));

        }

        if(userAccount == null){
            throw new BadCredentialsException("Bad credentials");
        }
        AbstractAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(userAccount.getUsername(),
                userAccount.getPassword(), Collections.emptyList());
        Authentication auth = authenticationManager.authenticate(authToken);
        return auth;
      }

      private String getUsername(final UserAccount creds) {
        if (creds != null) {
          return creds.getUsername();
        }
        return null;
      }



      @Override
      protected void successfulAuthentication(final HttpServletRequest req,
          final HttpServletResponse res, final FilterChain chain,
          final Authentication auth) throws IOException, ServletException {

        final UserAccount account = (UserAccount) auth.getPrincipal();
        jwtTokenService.addTokenToResponse(account, res);

        super.successfulAuthentication(req, res, chain, auth);
      }


}


JwtAuthorizationFilter.java

package com.example.securyti.security.jwt;

import java.io.IOException;
import java.util.ArrayList;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;

import com.example.securyti.config.ConfigurationService;

public class JwtAuthorizationFilter extends BasicAuthenticationFilter {
    private ConfigurationService configService;
    private JwtTokenService jwtTokenService;

    public JwtAuthorizationFilter(AuthenticationManager authManager, ConfigurationService configService,
            final JwtTokenService jwtTokenService) {
        super(authManager);
        this.configService = configService;
        this.jwtTokenService = jwtTokenService;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest req,
                                    HttpServletResponse res,
                                    FilterChain chain) throws IOException, ServletException {
        String header = req.getHeader(configService.getHeaderField());

        if (header == null || !header.startsWith(configService.getTokenPrefix())) {
            chain.doFilter(req, res);
            return;
        }

        UsernamePasswordAuthenticationToken authentication = getAuthentication(req);

        SecurityContextHolder.getContext().setAuthentication(authentication);
        chain.doFilter(req, res);
    }

    private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) {
        String token = request.getHeader(configService.getHeaderField());
        if (token != null) {
            // parse the token.
            String user = jwtTokenService.getUsernameFromJWT(token);

            if (user != null) {
                return new UsernamePasswordAuthenticationToken(user, null, new ArrayList<>());
            }
            return null;
        }
        return null;
    }
}


JwtTokenService.java(这只是帮助程序类)

package com.example.securyti.security.jwt;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import com.example.securyti.config.ConfigurationService;
import com.example.securyti.security.UserAccount;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.MalformedJwtException;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.SignatureException;
import io.jsonwebtoken.UnsupportedJwtException;

@Service
public class JwtTokenService {

    private static final Logger logger = LoggerFactory.getLogger(JwtTokenService.class);

    private ConfigurationService configurationService;

    public JwtTokenService(final ConfigurationService configurationService) {
        super();
        this.configurationService = configurationService;
    }

    String getTokenFromRequest(HttpServletRequest request) {
        String bearerToken = request.getHeader(configurationService.getHeaderField());
        if (StringUtils.hasText(bearerToken) && bearerToken.startsWith(configurationService.getTokenPrefix())) {
            return bearerToken.substring(7, bearerToken.length());
        }
        return null;
    }

    public void addTokenToResponse(UserAccount account, HttpServletResponse res) {

        LocalDateTime expiry = LocalDateTime.now().plusSeconds(configurationService.getJwtExpirationInSec());

        String token = Jwts.builder()
                .setSubject(account.getUsername())
                .setIssuedAt(new Date())
                .setExpiration(Date.from(expiry.atZone(ZoneId.systemDefault()).toInstant()))
                .signWith(SignatureAlgorithm.HS512, configurationService.getJwtSecret())
                .compact();
        res.addHeader(configurationService.getHeaderField(), configurationService.getTokenPrefix() + token);
    }

    public String getUsernameFromJWT(String token) {
        Claims claims = Jwts.parser()
                .setSigningKey(configurationService.getJwtSecret())
                .parseClaimsJws(token)
                .getBody();

        return claims.getSubject();
    }

    public boolean validateToken(String authToken) {
        try {
            Jwts.parser().setSigningKey(configurationService.getJwtSecret()).parseClaimsJws(authToken);
            return true;
        } catch (SignatureException ex) {
            logger.error("Invalid JWT signature");
        } catch (MalformedJwtException ex) {
            logger.error("Invalid JWT token");
        } catch (ExpiredJwtException ex) {
            logger.error("Expired JWT token");
        } catch (UnsupportedJwtException ex) {
            logger.error("Unsupported JWT token");
        } catch (IllegalArgumentException ex) {
            logger.error("JWT claims string is empty.");
        }
        return false;
    }
}


application.properties

spring.datasource.url= jdbc:mysql://localhost:3306/mydb
spring.datasource.username= root
spring.datasource.password= root

spring.jpa.hibernate.ddl-auto = update

#TRACE, DEBUG, INFO, WARN, ERROR, FATAL, OFF
logging.level.root=DEBUG

## JWT
jwt.secret= JWTSuperSecretKey
jwt.expirationInSec = 10
jwt.tokenPrefix = Bearer
jwt.headerField = Authorization


控制器中没有处理程序方法“ \ login”。目前,当我使用有效的用户名和密码点击“ / login”时,在控制台上收到403和以下消息:

Bad credentials
    at com.example.securyti.security.jwt.JwtAuthenticationFilter.attemptAuthentication(JwtAuthenticationFilter.java:58)


我想念什么。如果我的理解在某处碰巧是错误的,请纠正我。提前致谢。

最佳答案

为此,可能将Spring Cloud安全性与JWT令牌一起使用是更合适的选择。对于您的用例,您应该配置一个授权服务器,此工作在Spring Cloud Security中非常简单,该服务器将是如下所示的Spring Boot应用程序:

@Configuration
@EnableAuthorizationServer
public class SecurityOAuth2AutorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    private final AuthenticationManager authenticationManager;
    private final PasswordEncoder passwordEncoder;

    public SecurityOAuth2AutorizationServerConfig(AuthenticationManager authenticationManager,
                                                  PasswordEncoder passwordEncoder) {
        this.authenticationManager = authenticationManager;
        this.passwordEncoder = passwordEncoder;
    }

    @Bean
    public UserDetailsService accountUserDetailsService() {
        InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager();
        inMemoryUserDetailsManager.createUser(new User("user", passwordEncoder.encode("secret"),
                Collections.singleton(new SimpleGrantedAuthority("USER"))));

        return inMemoryUserDetailsManager;
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
        endpoints.approvalStoreDisabled()
                .authenticationManager(authenticationManager)
                .tokenStore(tokenStore())
                .accessTokenConverter(accessTokenConverter())
                .userDetailsService(accountUserDetailsService)
                .reuseRefreshTokens(false);
    }


    @Override
    public void configure(AuthorizationServerSecurityConfigurer oauthServer) {
        oauthServer.tokenKeyAccess("permitAll()")
                .passwordEncoder(passwordEncoder)
                .checkTokenAccess("isAuthenticated()")
                .allowFormAuthenticationForClients();
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                .withClient("client")
                .secret(passwordEncoder.encode("secret"))
                .authorizedGrantTypes("authorization_code", "refresh_token", "password").scopes("openid")
                .authorities("ROLE_USER", "ROLE_EMPLOYEE")
                .scopes("read", "write", "trust", "openid")
                .resourceIds("oauth2-resource")
                .autoApprove(true)
                .accessTokenValiditySeconds(5)
                .refreshTokenValiditySeconds(60*60*8);
    }

    @Bean
    public TokenStore tokenStore() {
        return new JwtTokenStore(accessTokenConverter());
    }

    @Bean
    public JwtAccessTokenConverter accessTokenConverter() {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setSigningKey("123");
        return converter;
    }

}


WebSecurityConfig类

@Configuration
@Order(SecurityProperties.DEFAULT_FILTER_ORDER)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable().httpBasic().disable()
                .formLogin().loginPage("/login").loginProcessingUrl("/login")
                .permitAll()
                .and()
                .requestMatchers().antMatchers("/account/userInfo", "/login", "/oauth/authorize", "/oauth/confirm_access")
                .and()
                .authorizeRequests().anyRequest().authenticated();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
}


pom.xml

<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>jwt-authserver</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>jwt-authserver</name>
    <description>Demo project for Spring Boot</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.3.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <spring-cloud.version>Finchley.RELEASE</spring-cloud.version>
    </properties>

    <dependencies>

        <dependency>
            <groupId>org.webjars</groupId>
            <artifactId>bootstrap</artifactId>
            <version>3.3.7-1</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>


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

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

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-oauth2</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

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


</project>


以及如下登录页面:

<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />

    <link rel="stylesheet" th:href="@{/webjars/bootstrap/3.3.7-1/css/bootstrap.css}"/>
    <link rel="stylesheet" th:href="@{/webjars/bootstrap/3.3.7-1/css/bootstrap-theme.css}"/>

    <title>Log In</title>
</head>
<body>
<div class="container">
    <form role="form" action="login" method="post">
        <div class="row">
            <div class="form-group">
                <div class="col-md-6 col-lg-6 col-md-offset-2 col-lg-offset-">
                    <label for="username">Username:</label>
                    <input type="text" class="form-control" id="username" name="username"/>
                </div>
            </div>
        </div>
        <div class="row">
            <div class="form-group">
                <div class="col-md-6 col-lg-6 col-md-offset-2 col-lg-offset-2">
                    <label for="password">Password:</label>
                    <input type="password" class="form-control" id="password" name="password"/>
                </div>
            </div>
        </div>
        <div class="row">
            <div class="col-md-offset-2 col-lg-offset-2 col-lg-12">
                <button type="submit" class="btn btn-primary">Submit</button>
            </div>
        </div>
    </form>
</div>

<script th:src="@{/webjars/jquery/3.2.0/jquery.min.js}"></script>
<script th:src="@{/webjars/bootstrap/3.3.7-1/js/bootstrap.js}" ></script>
</body>
</html>


这样,您就拥有了使用JWT令牌的完整授权服务器oauth2,即使看起来似乎付出了很大的努力,但我认为这是不使用自定义安全协议或路径的正确方法,因为这些将很快变得难以维护解决复杂的安全问题,使用标准的安全协议永远是最好的方法!

在客户端,您可以使用授权代码或密码oauth2标准流程或在客户端应用程序上使用WebSSO功能通过如下所示的简单配置自行实现Web sso:

@Configuration
@EnableOAuth2Sso
class OAuth2SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.csrf().disable().cors().and().httpBasic().disable()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
                .and()
                .authorizeRequests().anyRequest().authenticated();
    }
}


希望对您有所帮助

关于java - (Spring Boot + JWT)如何获得JWT的响应?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/53800099/

10-16 21:52