gpt4 book ai didi

没有 spring boot 的 Spring Security JWT 示例

转载 作者:行者123 更新时间:2023-12-04 10:41:26 26 4
gpt4 key购买 nike

我有一个 Spring 5 MVC/REST 应用程序。我使用 JPA Hibernate 作为 ORM。
我想保护我的应用程序。我看到的所有示例都基于 Spring Boot。
我想在我的应用程序中使用 jwt。我也想要没有 XML 配置。

( Spring MVC - 5.2.0.RELEASE )

我需要例子。谢谢

最佳答案

要使用 Spring Security 和 JWT 保护 Spring REST 应用程序,您可以按照以下步骤操作

  • 在 pom.xml 中添加以下依赖
    <dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-web</artifactId>
    <version>5.2.1.RELEASE</version>
    </dependency>
    <dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-config</artifactId>
    <version>5.2.1.RELEASE</version>
    </dependency>
    <dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.1</version>
    </dependency>
  • 创建 JwtAuthenticationEntryPoint 以拒绝每个未经身份验证的请求
    @Component
    public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint, Serializable {
    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException {
    response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
    }
    }
  • 创建模型类
    public class JwtRequest implements Serializable {
    private static final long serialVersionUID = 5926468583005150707L;
    private String username;
    private String password;

    // need default constructor for JSON Parsing
    public JwtRequest() {
    }

    public JwtRequest(String username, String password) {
    this.setUsername(username);
    this.setPassword(password);
    }
    // Getters and Setters
    }

    public class JwtResponse implements Serializable {
    private static final long serialVersionUID = -8091879091924046844L;
    private final String jwttoken;

    public JwtResponse(String jwttoken) {
    this.jwttoken = jwttoken;
    }

    public String getToken() {
    return this.jwttoken;
    }
    }
  • 为 token 生成和验证创建 Util 类
    @Component
    public class JwtTokenUtil implements Serializable {
    private static final long serialVersionUID = -2550185165626007488L;
    public static final long JWT_TOKEN_VALIDITY = 5 * 60 * 60;
    @Value("${jwt.secret}")
    private String secret;

    // retrieve username from jwt token
    public String getUsernameFromToken(String token) {
    return getClaimFromToken(token, Claims::getSubject);
    }

    // retrieve expiration date from jwt token
    public Date getExpirationDateFromToken(String token) {
    return getClaimFromToken(token, Claims::getExpiration);
    }

    public <T> T getClaimFromToken(String token, Function<Claims, T> claimsResolver) {
    final Claims claims = getAllClaimsFromToken(token);
    return claimsResolver.apply(claims);
    }

    // for retrieveing any information from token we will need the secret key
    private Claims getAllClaimsFromToken(String token) {
    return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
    }

    // check if the token has expired
    private Boolean isTokenExpired(String token) {
    final Date expiration = getExpirationDateFromToken(token);
    return expiration.before(new Date());
    }

    // generate token for user
    public String generateToken(UserDetails userDetails) {
    Map<String, Object> claims = new HashMap<>();
    return doGenerateToken(claims, userDetails.getUsername());
    }

    // while creating the token -
    // 1. Define claims of the token, like Issuer, Expiration, Subject, and the ID
    // 2. Sign the JWT using the HS512 algorithm and secret key.
    // 3. According to JWS Compact
    // Serialization(https://tools.ietf.org/html/draft-ietf-jose-json-web-signature-41#section-3.1)
    // compaction of the JWT to a URL-safe string
    private String doGenerateToken(Map<String, Object> claims, String subject) {
    return Jwts.builder().setClaims(claims).setSubject(subject).setIssuedAt(new Date(System.currentTimeMillis()))
    .setExpiration(new Date(System.currentTimeMillis() + JWT_TOKEN_VALIDITY * 1000)).signWith(SignatureAlgorithm.HS512, secret).compact();
    }

    // validate token
    public Boolean validateToken(String token, UserDetails userDetails) {
    final String username = getUsernameFromToken(token);
    return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
    }
    }
  • 创建 UserDetailsS​​ervice 和 UserRepository 实现以从数据库中按用户名加载用户
    @Service
    public class JwtUserDetailsService implements UserDetailsService {

    @Autowired
    private UserRepository repo;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    UserEntity user = repo.findByUsername(username);
    if (user != null) {
    return new User(user.getUsername(), user.getPassword(), new ArrayList<>());
    } else {
    throw new UsernameNotFoundException("User not found with username: " + username);
    }
    }
    }
  • 创建请求过滤器以检查请求是否具有有效的 JWT token 。如果它具有有效的 JWT token ,则在上下文中设置身份验证,以指定当前用户已通过身份验证。
    @Component
    public class JwtRequestFilter extends OncePerRequestFilter {
    @Autowired
    private JwtUserDetailsService jwtUserDetailsService;
    @Autowired
    private JwtTokenUtil jwtTokenUtil;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
    final String requestTokenHeader = request.getHeader("Authorization");
    String username = null;
    String jwtToken = null;
    // JWT Token is in the form "Bearer token". Remove Bearer word and get
    // only the Token
    if (requestTokenHeader != null &amp;&amp; requestTokenHeader.startsWith("Bearer ")) {
    jwtToken = requestTokenHeader.substring(7);
    try {
    username = jwtTokenUtil.getUsernameFromToken(jwtToken);
    } catch (IllegalArgumentException e) {
    System.out.println("Unable to get JWT Token");
    } catch (ExpiredJwtException e) {
    System.out.println("JWT Token has expired");
    }
    } else {
    logger.warn("JWT Token does not begin with Bearer String");
    }
    // Once we get the token validate it.
    if (username != null &amp;&amp; SecurityContextHolder.getContext().getAuthentication() == null) {
    UserDetails userDetails = this.jwtUserDetailsService.loadUserByUsername(username);
    // if token is valid configure Spring Security to manually set
    // authentication
    if (jwtTokenUtil.validateToken(jwtToken, userDetails)) {
    UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
    usernamePasswordAuthenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
    // After setting the Authentication in the context, we specify
    // that the current user is authenticated. So it passes the
    // Spring Security Configurations successfully.
    SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
    }
    }
    chain.doFilter(request, response);
    }
    }
  • 创建身份验证 Controller 以公开 POST API /认证它在正文中接受用户名和密码。我们使用 Spring Authentication Manager 验证用户名和密码。如果凭据有效,则使用 JwtTokenUtil 创建 JWT token 并将其提供给客户端。
        @RestController
    @CrossOrigin
    public class JwtAuthenticationController {
    @Autowired
    private AuthenticationManager authenticationManager;
    @Autowired
    private JwtTokenUtil jwtTokenUtil;
    @Autowired
    private JwtUserDetailsService userDetailsService;

    @PostMapping("/authenticate")
    public ResponseEntity<?> createAuthenticationToken(@RequestBody JwtRequest authenticationRequest) throws Exception {
    authenticate(authenticationRequest.getUsername(), authenticationRequest.getPassword());
    final UserDetails userDetails = userDetailsService.loadUserByUsername(authenticationRequest.getUsername());
    final String token = jwtTokenUtil.generateToken(userDetails);
    return ResponseEntity.ok(new JwtResponse(token));
    }

    private void authenticate(String username, String password) throws Exception {
    try {
    authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
    } catch (DisabledException e) {
    throw new Exception("USER_DISABLED", e);
    } catch (BadCredentialsException e) {
    throw new Exception("INVALID_CREDENTIALS", e);
    }
    }
    }
  • 配置 Spring Security
    @Configuration
    @ComponentScan(basePackages = "com.javachinna")
    @EnableWebSecurity
    public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
    @Autowired
    private UserDetailsService jwtUserDetailsService;
    @Autowired
    private JwtRequestFilter jwtRequestFilter;

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
    // configure AuthenticationManager so that it knows from where to load
    // user for matching credentials
    // Use BCryptPasswordEncoder
    auth.userDetailsService(jwtUserDetailsService).passwordEncoder(passwordEncoder());
    }

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

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

    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
    // We don't need CSRF for this example
    httpSecurity.csrf().disable()
    // dont authenticate this particular request
    .authorizeRequests().antMatchers("/authenticate").permitAll().
    // all other requests need to be authenticated
    anyRequest().authenticated().and().
    // make sure we use stateless session; session won't be used to
    // store user's state.
    exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint).and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    // Add a filter to validate the tokens with every request
    httpSecurity.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
    }
    }
  • 创建 SecurityWebApplicationInitializer 来注册 springSecurityFilterChain
    public class SecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer {

    }
  • 将 WebSecurityConfig 类添加到现有的 ApplicationInitializer
    public class SpringWebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
    @Override
    protected Class[] getRootConfigClasses() {
    return new Class[] { WebSecurityConfig.class };
    }
    // ... other overrides ...
    }
  • 将 key 添加到 application.properties 文件

    应用属性

    jwt.secret=javachinna

  • 就是这样。现在通过使用 url localhost:8080/authenticate 和请求正文中的有效用户名和密码创建 POST 请求来生成 JSON Web token 。在每个请求的请求 header 中使用此 token 。

    如需更多信息,请参阅 article on Securing Spring REST services using JWT without using spring boot .

    关于没有 spring boot 的 Spring Security JWT 示例,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59907402/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com