下面我将为你详细讲解“Java SpringSecurity+JWT实现登录认证”的完整攻略。 首先,让我们一步步来实现一个基于SpringSecurity和JWT的用户登录认证系统。整个实现过程包括三个步骤:
- 集成SpringSecurity和JWT
- 配置SpringSecurity
- 实现登录接口
接下来,我们将分别对这三个步骤进行讲解。
1. 集成SpringSecurity和JWT
我们使用Maven来管理项目依赖,因此需要在pom.xml文件中添加如下两个依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.2</version>
</dependency>
上述依赖中,spring-boot-starter-security是SpringSecurity的启动器,jjwt-api是JWT的API。
接下来,我们需要创建一个JWT工具类,用于生成和解析JWT,实现代码如下:
@Component
public class JwtTokenUtil {
private final String secret = "mySecret";
private final long expiration = 604800L;
public String generateToken(String username) {
Map<String, Object> claims = new HashMap<>();
claims.put("sub", username);
claims.put("created", new Date());
return Jwts.builder()
.setClaims(claims)
.setExpiration(new Date(System.currentTimeMillis() + expiration * 1000))
.signWith(SignatureAlgorithm.HS512, secret)
.compact();
}
public String getUsernameFromToken(String token) {
Claims claims = Jwts.parser()
.setSigningKey(secret)
.parseClaimsJws(token)
.getBody();
return claims.getSubject();
}
public boolean validateToken(String token) {
try {
Jwts.parser().setSigningKey(secret).parseClaimsJws(token);
return true;
} catch (ExpiredJwtException ex) {
System.out.println("Token已过期");
} catch (SignatureException ex) {
System.out.println("Token签名错误");
} catch (JwtException ex) {
System.out.println("Token解析错误");
} catch (Exception ex) {
System.out.println("其他错误");
}
return false;
}
}
上述代码中,核心的三个方法分别是:generateToken用于生成JWT,getUsernameFromToken用于从JWT中获取用户名,validateToken用于验证JWT是否有效。
2. 配置SpringSecurity
接下来,我们需要配置SpringSecurity,添加一个SecurityConfiguration类,实现代码如下:
@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Autowired
private JwtTokenUtil jwtTokenUtil;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("user")
.password("{noop}password")
.roles("USER");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/api/login").permitAll()
.anyRequest().authenticated()
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.addFilterBefore(new JwtAuthenticationFilter(jwtTokenUtil), UsernamePasswordAuthenticationFilter.class);
}
}
上述代码中,我们通过auth.inMemoryAuthentication()方式添加了一个用户,用户名为user,密码为password,角色为USER。configure(HttpSecurity http) 方法用于配置访问规则,我们允许/api/login接口不需要认证,其他接口需要进行认证。sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)方法用于设置Session的创建策略为不创建Session。
3. 实现登录接口
最后,我们需要实现一个/login接口,用于接受用户的用户名和密码,生成JWT,并返回给客户端。实现代码如下:
@RestController
@RequestMapping("/api")
public class LoginController {
@Autowired
private JwtTokenUtil jwtTokenUtil;
@RequestMapping(value = "/login", method = RequestMethod.POST)
public ResponseEntity<?> login(@RequestParam("username") String username, @RequestParam("password") String password) {
Authentication authentication = null;
try {
authentication = new UsernamePasswordAuthenticationToken(username, password);
authentication = authenticationManager().authenticate(authentication);
} catch (Exception e) {
return ResponseEntity.ok(new ApiResponse(false, "用户名或密码错误"));
}
SecurityContextHolder.getContext().setAuthentication(authentication);
String token = jwtTokenUtil.generateToken(username);
return ResponseEntity.ok(new JwtAuthenticationResponse(token));
}
@Autowired
public void configureAuthentication(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
authenticationManagerBuilder.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Autowired
private UserDetailsService userDetailsService;
}
上述代码中,我们通过/login接口接受到的用户名和密码,创建一个UsernamePasswordAuthenticationToken对象,并调用authenticationManager().authenticate(authentication)方法进行认证,如果认证成功,则生成JWT,并返回给客户端。
至此,我们就完成了一个基于SpringSecurity和JWT的用户登录认证系统。
下面,我来介绍两条关于该系统的使用示例:
- 使用Postman进行登录认证
在Postman中发送如下请求:
URL: http://localhost:8080/api/login
Method: POST
Body: x-www-form-urlencoded
- username: user
- password: password
可以得到如下JSON响应:
{
"token": "eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJ1c2VyIiwiaWF0IjoxNTkyNTIyNzg5LCJleHAiOjE1OTMxMDcyODl9.pjLckrJZL98QXFJ8UDrD7lmlfvI7GcJBAc5FAwVV-easGnaC1z-cQZ7zk4-tjOq7kQZN5yYBLaX10KjcOjSDcA"
}
可以看到,我们成功地获得了JWT。
- 使用SpringBoot集成测试进行登录认证
在创建SpringBoot集成测试时,需要在测试类上添加注解@WebMvcTest,用于启用Spring的Web应用程序上下文,同时会自动配置SpringMvc相依的Bean。
@RunWith(SpringRunner.class)
@WebMvcTest
public class LoginControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private JwtTokenUtil jwtTokenUtil;
@Test
public void testLoginSuccess() throws Exception {
when(jwtTokenUtil.generateToken(any())).thenReturn("test_token");
String requestBody = "{\"username\":\"user\",\"password\":\"password\"}";
mockMvc.perform(post("/api/login")
.contentType(MediaType.APPLICATION_JSON)
.content(requestBody))
.andExpect(status().isOk())
.andExpect(jsonPath("$.token", is("test_token")));
}
@Test
public void testLoginFailure() throws Exception {
String requestBody = "{\"username\":\"user\",\"password\":\"error_password\"}";
mockMvc.perform(post("/api/login")
.contentType(MediaType.APPLICATION_JSON)
.content(requestBody))
.andExpect(status().isOk())
.andExpect(jsonPath("$.success", is(false)))
.andExpect(jsonPath("$.message", is("用户名或密码错误")));
}
}
上述代码中,我们通过MockMvc对象模拟了一个HTTP请求,发送给/login接口,通过when(jwtTokenUtil.generateToken(any())).thenReturn("test_token")模拟了一个JWT。执行两个测试用例,分别测试了登录成功和登录失败的情况。
希望我的讲解可以帮助到你。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Java SpringSecurity+JWT实现登录认证 - Python技术站