如果您是JWTs Token的新手,我们将学习如何使用Spring安全性和JWT(JSON Web令牌)保护Spring启动REST API。 首先从Github Spring Boot REST API克隆以下springBoot项目,步骤:
- 添加Maven依赖项。
- 更新现有用户模型
- 更新用户存储库
- 使用继承Spring UserDetailsService实现UserDetailService
- 实现JWTAuthenticationFilter
- 实现JWTAuthorizationFilter
- 覆盖Spring Boot默认的Web安全配置
- 构建并运行项目。
- 测试
- 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> { /** * 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; /** * 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
测试:
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" } |