简体   繁体   English

(Spring Boot + JWT)如何获得JWT的响应?

[英](Spring Boot + JWT) how to get JWT in response?

I want to get the JWT in respose when I hit '/login'. 当我点击“ / login”时,我想重新放置JWT。 But I am missing something and can't figure it out. 但是我缺少一些东西,无法解决。

Following is my code: 以下是我的代码:

SecurytiApplication.java 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 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 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 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 (This is just the helper class) 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 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

There is no handler method '\\login' in controller. 控制器中没有处理程序方法“ \\ login”。 Currently when I hit the '/login' with valid username and password I get 403 with following message on console: 当前,当我使用有效的用户名和密码点击“ / login”时,在控制台上收到403和以下消息:

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

What am I missing. 我想念什么。 Please correct me if my understanding happen to be wrong somewhere. 如果我的理解在某处不对,请纠正我。 Thanks in advance. 提前致谢。

for this purpose probably use Spring Cloud security with JWT token is a more suitable choice. 为此,将Spring Cloud安全性与JWT令牌一起使用可能是一个更合适的选择。 For your use case you should configure an authorization server and this job is very simple in spring cloud security the server will be a spring boot app like below: 对于您的用例,您应该配置一个授权服务器,此工作在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 class 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 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>

and a login page like below: 和如下登录页面:

<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>

in this way you have a complete authorization server oauth2 that use JWT token even if it can to seem a too big effort it is in my opinion the correct way pleas do not use custom security protocol or path because those will became very soon hard to maintain and complex to fix security issues use a standard security protocol is always the best way! 这样,您就拥有了使用JWT令牌的完整授权服务器oauth2,即使看起来似乎付出了很大的努力,但我认为这是不使用自定义安全协议或路径的正确方法,因为这些将很快变得难以维护解决复杂的安全问题,使用标准的安全协议永远是最好的方法!

on the client side you can implement by your self a web sso using the authorization code or password oauth2 standard flow or using Spring cloud security on the client application using the WebSSO capabilities with a simple configuration like below: 在客户端上,您可以使用授权代码或密码oauth2标准流程或在客户端应用程序上使用WebSSO功能(如下所示)通过Spring Cloud安全性自行实现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();
    }
}

I hope that it can help you 希望对您有所帮助

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM