SpringBoot REST API + JWT + Spring Security教程
如果您是JWTs Token的新手,我們將學習如何使用Spring安全性和JWT(JSON Web令牌)保護Spring啟動REST API。
首先從Githubofollow,noindex" target="_blank">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 = <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
測試:
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>"[email protected]"</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>"[email protected]"</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> } POSThttp:</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>