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