如果您是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>