如何在 Spring Security 中实现基于 JWT 的身份验证和授权

     2023-02-27     61

关键词:

【中文标题】如何在 Spring Security 中实现基于 JWT 的身份验证和授权【英文标题】:How to implement JWT based authentication and authorization in Spring Security 【发布时间】:2018-08-25 19:30:37 【问题描述】:

如何在 Spring Security 中实现基于 JWT 的认证和授权

我正在尝试在我的 Spring Boot 应用程序中实现基于 jwt 的身份验证和授权。我遵循了编写here 的教程。但它在我的应用程序中没有做任何事情。它不返回 jwt 令牌,而是我通过了身份验证并且我的请求得到了满足。我是春季安全的新手。这是我的代码。

我希望我的应用返回 jwt 令牌并使用该令牌请求必须获得授权。

这是我的代码。

JWTAuthenticationFilter.java

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter 

    private AuthenticationManager authenticationManager;

    @Autowired
    CustomUserDetailsService userService;

    public JWTAuthenticationFilter(AuthenticationManager authenticationManager) 
    this.authenticationManager = authenticationManager;
    

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
        throws AuthenticationException 

    try 

        CustomUserDetails user = new ObjectMapper().readValue(request.getInputStream(), CustomUserDetails.class);

        return authenticationManager.authenticate(
            new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword(), new ArrayList<>()));
     catch (Exception e) 
    

    return super.attemptAuthentication(request, response);
    

    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
        Authentication auth) 

    String loggedInUser = ((CustomUserDetails) auth.getPrincipal()).getUsername();

    Claims claims = Jwts.claims().setSubject(loggedInUser);

    if (loggedInUser != null) 
        CustomUserDetails user = (CustomUserDetails) userService.loadUserByUsername(loggedInUser);
        String roles[] = ;

        for (Role role : user.getUser().getUserRoles()) 
        roles[roles.length + 1] = role.getRole();
        
        claims.put("roles", roles);

        claims.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME));

    

    String token = Jwts.builder().setClaims(claims)
        .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
        .signWith(SignatureAlgorithm.HS512, SECRET.getBytes()).compact();
    response.addHeader(HEADER_STRING, TOKEN_PREFIX + token);

    


JWTAuthorizationFilter.java

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;

public class JWTAuthorizationFilter extends BasicAuthenticationFilter 

    public JWTAuthorizationFilter(AuthenticationManager authenticationManager) 
    super(authenticationManager);

    

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

    String header = request.getHeader(HEADER_STRING);
    if (header == null || !header.startsWith(TOKEN_PREFIX)) 
        chain.doFilter(request, response);
        return;
    

    UsernamePasswordAuthenticationToken authentication = getToken(request);
    SecurityContextHolder.getContext().setAuthentication(authentication);
    chain.doFilter(request, response);

    

    @SuppressWarnings("unchecked")
    private UsernamePasswordAuthenticationToken getToken(HttpServletRequest request) 

    String token = request.getHeader(HEADER_STRING);

    System.out.println("-----------------------------------------------------");
    System.out.println("Token: " + token);
    System.out.println("-----------------------------------------------------");

    if (token != null) 

        Claims claims = Jwts.parser().setSigningKey(SECRET.getBytes())
            .parseClaimsJws(token.replace(TOKEN_PREFIX, "")).getBody();
        String user = claims.getSubject();

        ArrayList<String> roles = (ArrayList<String>) claims.get("roles");

        ArrayList<MyGrantedAuthority> rolesList = new ArrayList<>();

        if (roles != null) 
        for (String role : roles) 
            rolesList.add(new MyGrantedAuthority(role));
        
        
        if (user != null) 
        return new UsernamePasswordAuthenticationToken(user, null, null);
        
        return null;
    
    return null;
    


SecurityConfig.java

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

    @Qualifier("userDetailsService")
    @Autowired
    CustomUserDetailsService userDetailsService;

    @Autowired
    PasswordEncoder passwordEncoder;

    @Autowired
    AuthenticationManager authenticationManager;

    @Autowired
    JWTAuthenticationEntryPoint jwtAuthenticationEntryPoint;

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) 
    try 
        auth.userDetailsService(this.userDetailsService).passwordEncoder(passwordEncoder);

     catch (Exception e) 

    

    

    /*
     * @Autowired public void configureGlobal(AuthenticationManagerBuilder auth)
     * throws Exception 
     * auth.inMemoryAuthentication().withUser("student").password("student").roles(
     * "student").and().withUser("admin") .password("admin").roles("admin"); 
     */

    @Override
    protected void configure(HttpSecurity http) throws Exception 

    http.csrf().disable();
    // http.authorizeRequests().anyRequest().permitAll();

    // http.authorizeRequests().antMatchers("/api/**").permitAll();

    http.addFilter(new JWTAuthenticationFilter(authenticationManager));
    http.addFilter(new JWTAuthorizationFilter(authenticationManager));

    http.authorizeRequests().antMatchers("/api/student/**").hasAnyRole("STUDENT", "ADMIN");
    http.authorizeRequests().antMatchers("/api/admin/**").hasRole("ADMIN");
    http.authorizeRequests().antMatchers("/api/libararian/**").hasAnyRole("LIBRARIAN", "ADMIN");
    http.authorizeRequests().antMatchers("/api/staff/**").hasAnyRole("STAFF", "ADMIN");
    http.authorizeRequests().antMatchers("/api/teacher/**").hasAnyRole("TEACHER", "ADMIN");
    http.authorizeRequests().antMatchers("/api/parent/**").hasAnyRole("PARENT", "ADMIN");
    http.httpBasic().authenticationEntryPoint(jwtAuthenticationEntryPoint);

    http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);

    // http.formLogin().and().logout().logoutSuccessUrl("/login?logout").permitAll();

    


MyGrantedAuthority.java

    public class MyGrantedAuthority implements GrantedAuthority 

        String authority;

        MyGrantedAuthority(String authority) 
        this.authority = authority;
        

        @Override
        public String getAuthority() 
        // TODO Auto-generated method stub
        return authority;
        

    

JWTAuthenticationEntryPoint.java

@Component
public class JWTAuthenticationEntryPoint implements AuthenticationEntryPoint 

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception)
        throws IOException, ServletException 
    response.setStatus(403);
    response.setContentType(MediaType.APPLICATION_JSON_VALUE);

    String message;
    if (exception.getCause() != null) 
        message = exception.getCause().getMessage();
     else 
        message = exception.getMessage();
    
    byte[] body = new ObjectMapper().writeValueAsBytes(Collections.singletonMap("error", message));
    response.getOutputStream().write(body);
    


【问题讨论】:

当您尝试使用用户名和密码发布“/登录”路径时,响应是什么? 我遵循了这个教程,这让我的工作变得轻松。knowledgefactory.net/2020/12/… 【参考方案1】:

我还在我的项目中使用 jwt 身份验证,我可以看到您缺少应该在项目中使用的入口点。我会告诉你我是如何实现它的,看看它是否可以帮助你=)。 您需要实现一个 authenticationEntryPoint 以告诉代码如何完成身份验证。它可以添加在过滤器之后,在 http.authorizerequest 上,使用以下命令:

    .authenticationEntryPoint(jwtAuthEndPoint);

其中 jwtAuthEndPoint 是以下组件:

  @Component
  public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint 
  @Override
    public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse,
        AuthenticationException e) throws IOException, ServletException 
    httpServletResponse.setStatus(SC_FORBIDDEN);
    httpServletResponse.setContentType(MediaType.APPLICATION_JSON_VALUE);

    String message;
    if (e.getCause() != null) 
        message = e.getCause().getMessage();
     else 
        message = e.getMessage();
    
    byte[] body = new ObjectMapper().writeValueAsBytes(Collections.singletonMap("error", message));
    httpServletResponse.getOutputStream().write(body);
    

我还建议您看一下本教程,在这种情况下对我有很大帮助:https://sdqali.in/blog/2016/07/07/jwt-authentication-with-spring-web---part-4/

【讨论】:

我添加了上面的类。我还是被困住了。我真的没有从教程中理解这个概念。你能让我的代码工作吗?我对 Spring Security 及其机制知之甚少。请看我更新的代码。有什么我错过的吗? 谢谢,这个回信可以派上用场:)【参考方案2】:

我明白了。 我遵循了另一个教程,这使我的工作变得轻松。 这是完整的重写工作代码

TokenProvider.java

package com.cloudsofts.cloudschool.security;

import static com.cloudsofts.cloudschool.security.SecurityConstants.EXPIRATION_TIME;
import static com.cloudsofts.cloudschool.security.SecurityConstants.SECRET;

import java.util.ArrayList;
import java.util.Date;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

import com.cloudsofts.cloudschool.people.users.pojos.CustomUserDetails;
import com.cloudsofts.cloudschool.people.users.pojos.Role;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

@Component
public class TokenProvider 

    @Autowired
    CustomUserDetailsService userService;

    public String createToken(String username) 

    CustomUserDetails user = (CustomUserDetails) userService.loadUserByUsername(username);

    Claims claims = Jwts.claims().setSubject(username);

    ArrayList<String> rolesList = new ArrayList<String>();

    for (Role role : user.getUser().getUserRoles()) 
        rolesList.add(role.getRole());

    

    claims.put("roles", rolesList);

    String token = Jwts.builder().setClaims(claims)
        .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME)).setIssuedAt(new Date())
        .signWith(SignatureAlgorithm.HS512, SECRET).compact();

    return token;
    

    public Authentication getAuthentication(String token) 
    String username = Jwts.parser().setSigningKey(SECRET).parseClaimsJws(token).getBody().getSubject();
    UserDetails userDetails = this.userService.loadUserByUsername(username);

    return new UsernamePasswordAuthenticationToken(userDetails, "", userDetails.getAuthorities());
    

JWTFilter.java

package com.cloudsofts.cloudschool.security;

import static com.cloudsofts.cloudschool.security.SecurityConstants.HEADER_STRING;

import java.io.IOException;

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

import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.GenericFilterBean;

import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.MalformedJwtException;
import io.jsonwebtoken.SignatureException;
import io.jsonwebtoken.UnsupportedJwtException;

public class JWTFilter extends GenericFilterBean 

    public final static String AUTHORIZATION_HEADER = "Authorization";

    private final TokenProvider tokenProvider;

    public JWTFilter(TokenProvider tokenProvider) 
    this.tokenProvider = tokenProvider;
    

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain)
        throws IOException, ServletException 
    try 
        HttpServletRequest httpRequest = (HttpServletRequest) servletRequest;
        String jwt = resolveToken(httpRequest);
        if (jwt != null) 
        Authentication authentication = this.tokenProvider.getAuthentication(jwt);
        if (authentication != null) 
            SecurityContextHolder.getContext().setAuthentication(authentication);
        
        

        chain.doFilter(servletRequest, servletResponse);
     catch (ExpiredJwtException | UnsupportedJwtException | MalformedJwtException | SignatureException
        | UsernameNotFoundException e) 
        // Application.logger.info("Security exception ", e.getMessage());
        ((HttpServletResponse) servletResponse).setStatus(HttpServletResponse.SC_UNAUTHORIZED);
    
    

    private String resolveToken(HttpServletRequest request) 
    String bearerToken = request.getHeader(HEADER_STRING);
    if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) 
        return bearerToken.substring(7, bearerToken.length());
    
    return null;
    


JWTConfigurer.java

import org.springframework.security.config.annotation.SecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.DefaultSecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

public class JWTConfigurer extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> 

    private final TokenProvider tokenProvider;

    public JWTConfigurer(TokenProvider tokenProvider) 
    this.tokenProvider = tokenProvider;
    

    @Override
    public void configure(HttpSecurity http) throws Exception 

    JWTFilter customFilter = new JWTFilter(tokenProvider);
    http.addFilterBefore(customFilter, UsernamePasswordAuthenticationFilter.class);
    


SecurityConfig.java

import javax.servlet.http.HttpServletResponse;

import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import com.cloudsofts.cloudschool.people.users.pojos.User;

@RestController
public class LoginController 

    private AuthenticationManager authenticationManager;

    private TokenProvider tokenProvider;

    private CustomUserDetailsService userService;

    LoginController(AuthenticationManager auth, CustomUserDetailsService service, TokenProvider tokenProvider) 
    this.authenticationManager = auth;
    this.userService = service;
    this.tokenProvider = tokenProvider;
    

    @PostMapping("/login")
    public String getToken(@RequestBody User user, HttpServletResponse response) 

    UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(user.getUsername(),
        user.getPassword());

    authenticationManager.authenticate(authToken);
    return tokenProvider.createToken(user.getUsername());

    

LoginController.java

import javax.servlet.http.HttpServletResponse;

import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import com.cloudsofts.cloudschool.people.users.pojos.User;

@RestController
public class LoginController 

    private AuthenticationManager authenticationManager;

    private TokenProvider tokenProvider;

    private CustomUserDetailsService userService;

    LoginController(AuthenticationManager auth, CustomUserDetailsService service, TokenProvider tokenProvider) 
    this.authenticationManager = auth;
    this.userService = service;
    this.tokenProvider = tokenProvider;
    

    @PostMapping("/login")
    public String getToken(@RequestBody User user, HttpServletResponse response) 

    UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(user.getUsername(),
        user.getPassword());

    authenticationManager.authenticate(authToken);
    return tokenProvider.createToken(user.getUsername());

    

Link to the Github project that helped me.

【讨论】:

如何使用 spring-security-core-ldap 插件在 grails 中实现 LDAP 身份验证?

】如何使用spring-security-core-ldap插件在grails中实现LDAP身份验证?【英文标题】:HowtoimplementLDAPauthenticationingrailswithspring-security-core-ldapplugin?【发布时间】:2012-02-1909:44:11【问题描述】:我是grails的新手,正在尝试实现LDAP身份验证... 查看详情

如何在 Spring Boot 中实现基于角色权限的系统

】如何在SpringBoot中实现基于角色权限的系统【英文标题】:HowtoimplementRolePermissionbasedsysteminSpringBoot【发布时间】:2016-11-0919:14:30【问题描述】:我正在尝试使用springboot和springsecurity来实现基于角色权限的系统。为此,我以http://w... 查看详情

如何在基于 Keycloak/Spring 的应用程序中实现单点注销?

】如何在基于Keycloak/Spring的应用程序中实现单点注销?【英文标题】:HowtoachieveSingleSign-OutinKeycloak/Springbasedapplications?【发布时间】:2018-11-1601:51:48【问题描述】:我有2个Spring网络应用程序。我正在使用Keycloak来保护它们。在Keycl... 查看详情

在 Spring Security 2.06 中实现自定义 AuthenticationProvider

】在SpringSecurity2.06中实现自定义AuthenticationProvider【英文标题】:ImplementcustomAuthenticationProviderinSpringSecurity2.06【发布时间】:2012-01-2819:21:20【问题描述】:我正在使用SpringSecurity来保护Struts2Web应用程序。由于项目限制,我使用的... 查看详情

LDAP 是不是支持 BCrypt?尝试在 Java Spring Security 中实现 BCrypt

】LDAP是不是支持BCrypt?尝试在JavaSpringSecurity中实现BCrypt【英文标题】:DoesLDAPSupportBCrypt?TryingtoimplementBCryptinJavaSpringSecurityLDAP是否支持BCrypt?尝试在JavaSpringSecurity中实现BCrypt【发布时间】:2014-10-1222:09:41【问题描述】:我目前正... 查看详情

Spring Security 基于角色的授权问题

】SpringSecurity基于角色的授权问题【英文标题】:SpringSecurityrole-basedauthorisationissue【发布时间】:2016-07-0121:11:23【问题描述】:我已经在我的应用程序中实现了SpringSecurity。它是基于无状态令牌的身份验证和基于用户名/密码的身... 查看详情

如何在微服务架构中实现基于角色的安全性

】如何在微服务架构中实现基于角色的安全性【英文标题】:howtoimplementrole-basedsecurityinmicroservicesarchitecture[closed]【发布时间】:2020-05-0406:45:32【问题描述】:我有一个带有4个微服务、eureka服务器和一个集中式API网关的spring-boot... 查看详情

Spring Security 使用基于xml的http认证

】SpringSecurity使用基于xml的http认证【英文标题】:Springsecurityusinghttpauthenticationbasedonxml【发布时间】:2015-02-2213:51:07【问题描述】:我正在尝试在我的项目中实现弹簧安全性,我的要求是第一个用户将使用将生成安全代码的url登... 查看详情

如何在spring-security中切换角色

几天来我一直在苦苦思索如何通过SpringSecurity在SpringBoot应用程序中实现与一个userAccount的角色之间的切换。在我的案例中有一个例子:我有一个类[UserAccount]{Id,username,password,isActive}和[Role]{Id,roleName,description},我正在用一些... 查看详情

如何在 Spring Boot 应用程序中关闭 Spring Security

】如何在SpringBoot应用程序中关闭SpringSecurity【英文标题】:HowtoturnoffSpringSecurityinSpringBootApplication【发布时间】:2017-03-0619:09:51【问题描述】:我已经使用SpringSecurity在我的SpringBoot应用程序中实现了身份验证。控制身份验证的主... 查看详情

如何在 Spring Boot 中实现刷新令牌

】如何在SpringBoot中实现刷新令牌【英文标题】:HowtoimplementrefreshtokeninSpringBoot【发布时间】:2018-09-2709:08:06【问题描述】:我已按照本指南https://auth0.com/blog/implementing-jwt-authentication-on-spring-boot/在我的Web应用程序中实现访问令牌... 查看详情

如何在基于 JWT 的单点登录身份验证架构中实现注销?

】如何在基于JWT的单点登录身份验证架构中实现注销?【英文标题】:HowtoimplementLogoutinaJWTbasedSingleSignOnauthenticationarchitecture?【发布时间】:2016-02-2707:23:30【问题描述】:我正在尝试了解如何使用JsonWeb令牌在单点登录架构中实现... 查看详情

如何在url上实现spring security

】如何在url上实现springsecurity【英文标题】:Howtoimplementspringsecurityontheurl【发布时间】:2018-11-2822:52:26【问题描述】:我想为这个应用程序实现springsecurity,这样用户只需更改url就可以访问管理页面。我还没有找到一个适合这个... 查看详情

在我的(java spring mvc + mysql application,thymeleaf)中实现spring security之后,身份验证发生了一些奇怪的事情

】在我的(javaspringmvc+mysqlapplication,thymeleaf)中实现springsecurity之后,身份验证发生了一些奇怪的事情【英文标题】:Afterimplementingspringsecurityinmy(javaspringmvc+mysqlapplication,thymeleaf)somethingweirdhappenswiththeauthentication【发布时间】:2018-... 查看详情

如何在 Firebase 中实现基于角色的访问控制

】如何在Firebase中实现基于角色的访问控制【英文标题】:howdoIimplementrolebasedaccesscontrolinfirebase【发布时间】:2013-10-3111:39:04【问题描述】:这是我第一次涉足Firebase和nosql,我来自SQL背景。使用简单登录安全电子邮件/密码,如何... 查看详情

如何在 Flask 中实现基于角色的访问控制?

】如何在Flask中实现基于角色的访问控制?【英文标题】:HowtoimplementrolebasedaccesscontrolinFlask?【发布时间】:2020-12-1209:52:15【问题描述】:是否有任何积极维护的插件可以帮助我创建具有基于角色的访问控制的Flask应用程序?例如... 查看详情

如何在基于 SQLite 的 recyclerview 中实现 searchview? - 安卓

】如何在基于SQLite的recyclerview中实现searchview?-安卓【英文标题】:HowtoimplementsearchviewinrecyclerviewwhichbasedonSQLite?-Android【发布时间】:2021-11-0506:45:11【问题描述】:我正在构建Android应用程序,它将患者详细信息存储在SQLite数据库... 查看详情

如何在 Spring Boot 中实现 Camunda SendTask

】如何在SpringBoot中实现CamundaSendTask【英文标题】:HowtoimplementCamundaSendTaskinSpringBoot【发布时间】:2020-10-0914:06:21【问题描述】:我是Camunda的新手,正在使用以下任务:业务规则任务-->(网关)-->发送任务-->用户任务在使... 查看详情