没有 Spring Boot 的 Spring Security JWT 示例
Posted
技术标签:
【中文标题】没有 Spring Boot 的 Spring Security JWT 示例【英文标题】:Spring Security JWT Example without spring boot 【发布时间】:2020-05-11 10:15:58 【问题描述】:我有一个 Spring 5 MVC/REST 应用程序。我使用 JPA Hibernate 作为 ORM。 我想保护我的应用程序。我看到的所有示例都是基于 Spring Boot。 我想在我的应用程序中使用 jwt。我也想要没有 XML Config。
(Spring MVC - 5.2.0.RELEASE)
我需要示例。谢谢
【问题讨论】:
这是您要找的吗?:baeldung.com/spring-security-oauth-jwt 感谢亲爱的@R.G,但我想要没有 Spring boot 的示例 @mohammad_soleimani 你是否设法在没有弹簧靴的情况下找到了解决方案。 亲爱的@oOXAam,现在GitHub上有一个例子 【参考方案1】:要使用 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;
创建用于令牌生成和验证的 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));
创建 UserDetailsService 和 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 令牌。如果它具有有效的 JWT Token,则在上下文中设置 Authentication,以指定当前用户已通过身份验证。
@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 && 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 && 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);
创建一个身份验证控制器以公开一个 POST API /authenticate,该 API 在正文中接受用户名和密码。使用 Spring Authentication Manager 我们验证用户名和密码。如果凭据有效,则使用 JwtTokenUtil 创建 JWT 令牌并提供给客户端。
@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 安全
@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 ...
将密钥添加到 application.properties 文件中
应用属性
jwt.secret=javachinna
就是这样。现在通过使用 url localhost:8080/authenticate 和请求正文中的有效用户名和密码创建 POST 请求来生成 JSON Web 令牌。在每个请求的请求标头中使用此令牌。
有关详细信息,请参阅article on Securing Spring REST services using JWT without using spring boot。
【讨论】:
虽然此链接可能会回答问题,但最好在此处包含答案的基本部分并提供链接以供参考。如果链接页面发生更改,仅链接答案可能会失效。 - From Review 我已在此处包含了答案的基本部分,并提供了链接以供参考。【参考方案2】:我编写了一个示例,用于使用 Spring security 5.0.0 none boot 和 JWT,dynamic Role。 我希望它很好
-
Spring MVC 5.0.0
Spring 安全 5.0.0
休眠 JPA 5.2.11
我的数据库是 Oracle 数据库
你可以下载它: download from github
【讨论】:
以上是关于没有 Spring Boot 的 Spring Security JWT 示例的主要内容,如果未能解决你的问题,请参考以下文章
带有undertow servlet容器的spring-boot应用程序中的“没有共同的密码套件”错误
SpringBoot :Spring boot 中 Redis 的使用
springboot - SqlSessionFactoryBean falls in circular dependencies by Spring Boot's DataSourceIni