Spring Cloud Security的核心组件-JWT

2023-04-13 07:19:46 浏览数 (2)

Spring Cloud Security是基于Spring Cloud的安全解决方案,它提供了很多功能模块,包括OAuth2、JWT、Session等。其中JWT(JSON Web Token)是一种轻量级的身份认证和授权机制,它是目前比较流行的一种Token机制,可以方便地实现无状态的身份认证和授权。

Spring Cloud Security中的JWT核心组件主要包括:

  1. JwtAuthenticationFilter:用于拦截请求并解析JWT Token,然后将用户信息设置到SecurityContext中;
  2. JwtTokenProvider:用于生成、解析和验证JWT Token;
  3. JwtConfig:用于配置JWT的相关参数,例如签名密钥、过期时间等。

下面我们将详细介绍如何在Spring Cloud Security中使用JWT。

1. 添加依赖

在使用Spring Cloud Security JWT之前,需要添加以下依赖:

代码语言:javascript复制
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-security</artifactId>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.1</version>
</dependency>

其中,spring-cloud-starter-security是Spring Cloud Security的基础依赖,jjwt是JWT的Java实现。

2. 配置JwtConfig

在使用JWT之前,需要配置JwtConfig,例如:

代码语言:javascript复制
@Configuration
public class JwtConfig {

    @Value("${jwt.secret}")
    private String secret;

    @Value("${jwt.expiration}")
    private int expiration;

    public String getSecret() {
        return secret;
    }

    public int getExpiration() {
        return expiration;
    }
}

其中,jwt.secret是JWT的签名密钥,jwt.expiration是JWT的过期时间(单位为秒)。

3. 实现JwtTokenProvider

接下来,需要实现JwtTokenProvider,例如:

代码语言:javascript复制
@Component
public class JwtTokenProvider {

    private final JwtConfig jwtConfig;

    @Autowired
    public JwtTokenProvider(JwtConfig jwtConfig) {
        this.jwtConfig = jwtConfig;
    }

    public String generateToken(UserDetails userDetails) {
        Date now = new Date();
        Date expiration = new Date(now.getTime()   jwtConfig.getExpiration() * 1000);

        return Jwts.builder()
                .setSubject(userDetails.getUsername())
                .setIssuedAt(now)
                .setExpiration(expiration)
                .signWith(SignatureAlgorithm.HS512, jwtConfig.getSecret())
                .compact();
    }

    public Authentication getAuthentication(String token) {
        UserDetails userDetails = getUserDetails(token);
        return new UsernamePasswordAuthenticationToken(userDetails, "", userDetails.getAuthorities());
    }

    public UserDetails getUserDetails(String token) {
        String username = getUsername(token);
        return new User(username, "", new ArrayList<>());
    }

    public boolean validateToken(String token) {
        try {
            Jwts.parser().setSigningKey(jwtConfig.getSecret()).parseClaimsJws(token);
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    private String getUsername(String token) {
        return Jwts.parser().setSigningKey(jwtConfig.getSecret()).parseClaimsJws(token).getBody().getSubject();
    }
}

在上面的代码中,generateToken方法用于生成JWT Token,其中包括用户名、颁发时间、过期时间等信息;getAuthentication方法用于根据Token获取用户信息,并将其封装成Authentication对象;getUserDetails方法用于根据Token获取用户详细信息;validateToken方法用于验证Token是否有效;getUsername方法用于根据Token获取用户名。

需要注意的是,以上代码中的UserDetails、User等类需要根据实际情况进行修改。

4. 实现JwtAuthenticationFilter

接下来,需要实现JwtAuthenticationFilter,用于拦截请求并解析JWT Token,例如:

代码语言:javascript复制
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {

    private final JwtTokenProvider jwtTokenProvider;

    @Autowired
    public JwtAuthenticationFilter(JwtTokenProvider jwtTokenProvider) {
        this.jwtTokenProvider = jwtTokenProvider;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String token = getToken(request);
        if (StringUtils.hasText(token) && jwtTokenProvider.validateToken(token)) {
            Authentication authentication = jwtTokenProvider.getAuthentication(token);
            SecurityContextHolder.getContext().setAuthentication(authentication);
        }
        filterChain.doFilter(request, response);
    }

    private String getToken(HttpServletRequest request) {
        String bearerToken = request.getHeader("Authorization");
        if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
            return bearerToken.substring(7);
        }
        return null;
    }
}

以上代码中的JwtAuthenticationFilter用于拦截请求,并从请求头中获取Token,然后根据Token获取用户信息并设置到SecurityContext中。

5. 配置WebSecurityConfigurerAdapter

最后,需要配置WebSecurityConfigurerAdapter,例如:

代码语言:javascript复制
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    private final JwtAuthenticationFilter jwtAuthenticationFilter;

    @Autowired
    public SecurityConfig(JwtAuthenticationFilter jwtAuthenticationFilter) {
        this.jwtAuthenticationFilter = jwtAuthenticationFilter;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
                .authorizeRequests()
                .antMatchers(HttpMethod.POST, "/api/authenticate").permitAll()
                .anyRequest().authenticated();
        http.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
    }
}

以上代码中的SecurityConfig继承自WebSecurityConfigurerAdapter,用于配置Spring Security的行为。在这里,我们将/api/authenticate接口设置为不需要认证,其他接口需要认证。同时,将JwtAuthenticationFilter添加到Spring Security的过滤器链中,用于拦截请求并进行身份认证。

6. 测试

最后,我们可以使用Postman等工具进行测试。首先,需要调用/authenticate接口进行身份认证,例如:

代码语言:javascript复制
POST /api/authenticate HTTP/1.1
Host: localhost:8080
Content-Type: application/json
Cache-Control: no-cache

{
    "username": "admin",
    "password": "123456"
}

如果身份认证成功,服务器将返回一个JWT Token,例如:

代码语言:javascript复制
{
    "token": "eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJhZG1pbiIsImlhdCI6MTYxOTAxMDU0NSwiZXhwIjoxNjE5MDE0MTQ1fQ.h3x1foXYjxxzV85KM2F5R5_NyfE-MrjK9Xr5PhdRjvCUZ0l0CwzvmJlmyc8t3qyA68SlypjN9F2Gme8Iv7Q2w"
}

以上代码中的token就是生成的JWT Token,我们可以在后续请求中使用它来进行身份认证。

接下来,我们可以使用JWT Token进行身份认证,例如:

代码语言:javascript复制
GET /api/users HTTP/1.1
Host: localhost:8080
Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJhZG1pbiIsImlhdCI6MTYxOTAxMDU0NSwiZXhwIjoxNjE5MDE0MTQ1fQ.h3x1foXYjxxzV85KM2F5R5_NyfE-MrjK9Xr5PhdRjvCUZ0l0CwzvmJlmyc8t3qyA68SlypjN9F2Gme8Iv7Q2w
Cache-Control: no-cache

如果JWT Token有效,服务器将返回用户信息,例如:

代码语言:javascript复制
[
    {
        "id": 1,
        "username": "admin",
        "password": "$2a$10$tGWfb3JyB1nS.8ETWfMyFeXGllq0Uh0jDslscfIMC.hgRt/Sz2tFe",
        "email": "admin@example.com",
        "roles": [
            {
                "id": 1,
                "name": "ROLE_ADMIN"
            }
        ]
    },
    {
        "id": 2,
        "username": "user",
        "password": "$2a$10$wKgfnw1NfA.3vg.1Akrn3.xfb.lGKFDxvyF8n7rQm1LkLLtVl15Ky",
        "email": "user@example.com",
        "roles": [
            {
                "id": 2,
                "name": "ROLE_USER"
            }
        ]
    }
]

0 人点赞