如果您是JWTs Token的新手,我们将学习如何使用Spring安全性和JWT(JSON Web令牌)保护Spring启动REST API。
首先从Github Spring Boot REST API克隆以下springBoot项目,步骤:- 添加Maven依赖项。
- 更新现有用户模型
- 更新用户存储库
- 使用继承Spring UserDetailsService实现UserDetailService
- 实现JWTAuthenticationFilter
- 实现JWTAuthorizationFilter
- 覆盖Spring Boot默认的Web安全配置
- 构建并运行项目。
- 测试
添加所需的Maven依赖关系
我们需要为现有的项目pom添加两个依赖项- spring-boot-starter-security
- java-jwt
<!-- Spring Boot Security dependency--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <!-- Java JWT dependency--> <dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>3.4.1</version> </dependency>
|
更新现有用户模型
接下来使用新属性用户名和密码更新现有用户模型,如下所示:
@Column(name = "username", nullable = false, unique = true) private String username;
@Column(name = "password", nullable = false) private String password;
|
更新现有的用户存储库
使用findByUsername方法更新现有的用户存储库。
public interface UserRepository extends JpaRepository<User, Long> {
/<strong> * Find by username user. * * @param username the username * @return the user */ User findByUsername(String username); }
|
使用继承Spring UserDetailsService实现UserDetailService
为了使Spring Security能够认证用户详细信息,我们需要实现UserDetailService并覆盖loadUserByUsername以根据数据库验证用户详细信息。
@Service public class UserDetailServiceImpl implements UserDetailsService {
private UserRepository userRepository;
/</strong> * Instantiates a new User detail service. * * @param userRepository the user repository */ public UserDetailServiceImpl(UserRepository userRepository) { this.userRepository = userRepository; }
@Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User userDetails = userRepository.findByUsername(username); if(userDetails == null){ throw new UsernameNotFoundException(username); } return new org.springframework.security.core.userdetails.User(userDetails.getUsername(),userDetails.getPassword(), new ArrayList<>()); } }
|
实现JWTAuthenticationFilter:
public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
public static final String SECRET = "SecretKeyToGenJWTs"; public static final long EXPIRATION_TIME = 864_000_000; // 10 days public static final String TOKEN_PREFIX = "Bearer "; public static final String HEADER_STRING = "Authorization";
private AuthenticationManager authenticationManager;
public JWTAuthenticationFilter(AuthenticationManager authenticationManager) { this.authenticationManager = authenticationManager; }
@Override public Authentication attemptAuthentication( HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { try {
com.staxrt.tutorial.model.User loginUser = new ObjectMapper().readValue(request.getInputStream(), com.staxrt.tutorial.model.User.class);
return authenticationManager.authenticate( new UsernamePasswordAuthenticationToken( loginUser.getUsername(), loginUser.getPassword(), new ArrayList<>())); } catch (IOException e) { throw new RuntimeException(e); } }
@Override protected void successfulAuthentication( HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException { String token = JWT.create() .withSubject( ((User) authResult.getPrincipal()).getUsername()) // Payload register sub claim .withExpiresAt( new Date(System.currentTimeMillis() + EXPIRATION_TIME)) // JWT token validity time .sign(Algorithm.HMAC512(SECRET.getBytes())); // JWT Signature response.addHeader(HEADER_STRING, TOKEN_PREFIX + token); } }
|
实现JWTAuthorizationFilter
public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
public static final String SECRET = "SecretKeyToGenJWTs"; public static final long EXPIRATION_TIME = 864_000_000; // 10 days public static final String TOKEN_PREFIX = "Bearer "; public static final String HEADER_STRING = "Authorization";
private AuthenticationManager authenticationManager;
public JWTAuthenticationFilter(AuthenticationManager authenticationManager) { this.authenticationManager = authenticationManager; }
@Override public Authentication attemptAuthentication( HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { try {
com.staxrt.tutorial.model.User loginUser = new ObjectMapper().readValue(request.getInputStream(), com.staxrt.tutorial.model.User.class);
return authenticationManager.authenticate( new UsernamePasswordAuthenticationToken( loginUser.getUsername(), loginUser.getPassword(), new ArrayList<>())); } catch (IOException e) { throw new RuntimeException(e); } }
@Override protected void successfulAuthentication( HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException { String token = JWT.create() .withSubject( ((User) authResult.getPrincipal()).getUsername()) // Payload register sub claim .withExpiresAt( new Date(System.currentTimeMillis() + EXPIRATION_TIME)) // JWT token validity time .sign(Algorithm.HMAC512(SECRET.getBytes())); // JWT Signature response.addHeader(HEADER_STRING, TOKEN_PREFIX + token); } }
|
覆盖Spring Boot默认的Web安全配置
这样我们可以将自己的身份认证逻辑注入JWT toknbase
@EnableWebSecurity public class WebSecurity extends WebSecurityConfigurerAdapter {
public static final String SIGN_UP_URL = "/api/v1/users";
@Autowired private UserDetailServiceImpl userDetailService;
@Autowired private BCryptPasswordEncoder bCryptPasswordEncoder;
public WebSecurity( UserDetailServiceImpl userDetailService, BCryptPasswordEncoder bCryptPasswordEncoder) { this.userDetailService = userDetailService; this.bCryptPasswordEncoder = bCryptPasswordEncoder; }
@Override protected void configure(HttpSecurity http) throws Exception { http.cors() .and() .csrf() .disable() .authorizeRequests() // Add a new custom security filter .antMatchers(HttpMethod.POST, SIGN_UP_URL) .permitAll() // Only Allow Permission for create user endpoint .anyRequest() .authenticated() .and() .addFilter(this.getJWTAuthenticationFilter()) // Add JWT Authentication Filter .addFilter( new JWTAuthorizationFilter(authenticationManager())) // Add JWT Authorization Filter .sessionManagement() .sessionCreationPolicy( SessionCreationPolicy.STATELESS); // this disables session creation on Spring Security }
@Bean CorsConfigurationSource corsConfigurationSource() { final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration( "/**", new CorsConfiguration() .applyPermitDefaultValues()); // Allow/restrict our CORS permitting requests from any // source return source; }
public JWTAuthenticationFilter getJWTAuthenticationFilter() throws Exception { final JWTAuthenticationFilter filter = new JWTAuthenticationFilter(authenticationManager()); filter.setFilterProcessesUrl("/api/v1/auth/login"); // override the default spring login url return filter; } }
|
构建并运行项目
mvn package
java -jar target/spring-boot-rest-api-auth-jwt-tutorial-0.0.1-SNAPSHOT.jar
或者,您可以在不使用package 的情况下运行应用程序 -
mvn spring-boot:run
运行http://localhost:8080.
测试:
POST http://localhost:8080/api/v1/users
Request { "username": "givantha90", "password": "welcome@123", "firstName": "Givantha", "lastName": "Kalansuriya", "email": "givanhta@gmail.com", "createdBy": "Givantha", "updatedBy": "Givantha" }
Resonse { "id": 13, "username": "givantha9110", "firstName": "Givantha", "lastName": "Kalansuriya", "email": "givanhta@gmail.com", "createdAt": "2018-11-24T15:20:19.463+0000", "createdBy": "Givantha", "updatedAt": "2018-11-24T15:20:19.463+0000", "updatedBy": "Givantha" }
POST http://localhost:8080/api/v1/auth/login
Request
{ "username": "givantha12", "password": "welcome@123" }
|