下面就给您详细讲解“SpringBoot整合SpringSecurity实现JWT认证的项目实践”的完整攻略。
一、什么是JWT
JWT(JSON Web Token)是一种用于对信息进行安全传输的开放标准,它将信息进行编码后生成一段字符串,用于在不同业务系统之间传递信息。在进行身份验证时,JWT通常被用于对用户进行身份认证和授权,它被广泛地应用于多语言和多平台上简化身份验证和授权流程。
二、为什么要使用JWT
相较于传统的Session和Cookie认证方式,使用JWT认证具有以下优势:
-
简化开发过程,开发人员只需要实现JWT生成和校验逻辑即可,避免了很多繁琐的Session和Cookie管理任务。
-
分布式应用友好,在多个服务器之间共享认证状态变得更加容易。
-
安全性高,JWT使用基于密钥的认证方式,可以防止中间人攻击和数据篡改。
三、SpringBoot整合SpringSecurity实现JWT认证的流程
- 引入相关依赖
在使用SpringBoot整合SpringSecurity实现JWT认证之前,我们需要在项目中引入一些相关依赖,包括:
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
- 实现用户认证逻辑
在进行JWT认证之前,我们需要先实现用户认证逻辑。可以通过继承SpringSecurity提供的UserDetailsService接口,实现自定义的用户认证逻辑。
@Service
public class MyUserDetailsService implements UserDetailsService {
@Autowired
private UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByUsername(username);
if (user == null) {
throw new UsernameNotFoundException(username + " not found");
}
List<SimpleGrantedAuthority> authorities = new ArrayList<>();
for (Role role : user.getRoles()) {
authorities.add(new SimpleGrantedAuthority(role.getName()));
}
return new org.springframework.security.core.userdetails.User(user.getUsername(),
user.getPassword(), authorities);
}
}
- 实现JWT生成和校验逻辑
使用JWT进行身份认证和授权,需要实现JWT的生成和校验逻辑。这里我们可以使用jjwt库进行实现。
@Service
public class JwtTokenProvider {
private static final Logger logger = LoggerFactory.getLogger(JwtTokenProvider.class);
private static String jwtSecret = "jwtSecret";
private static int jwtExpirationInMs = 604800000;
public static String generateToken(Authentication authentication) {
User principal = (User) authentication.getPrincipal();
Date expirationDate = new Date(System.currentTimeMillis() + jwtExpirationInMs);
return Jwts.builder()
.setSubject(principal.getUsername())
.setIssuedAt(new Date())
.setExpiration(expirationDate)
.signWith(SignatureAlgorithm.HS512, jwtSecret)
.compact();
}
public static boolean validateToken(String authToken) {
try {
Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(authToken);
return true;
} catch (SignatureException ex) {
logger.error("Invalid JWT signature");
} catch (MalformedJwtException ex) {
logger.error("Invalid JWT token");
} catch (ExpiredJwtException ex) {
logger.error("Expired JWT token");
} catch (UnsupportedJwtException ex) {
logger.error("Unsupported JWT token");
} catch (IllegalArgumentException ex) {
logger.error("JWT claims string is empty.");
}
return false;
}
public static String getUsernameFromToken(String token) {
Claims claims = Jwts.parser()
.setSigningKey(jwtSecret)
.parseClaimsJws(token)
.getBody();
return claims.getSubject();
}
}
- 配置JWT认证过滤器
在SpringSecurity中,我们可以通过实现一个过滤器来进行JWT认证,代码如下:
public class JwtAuthenticationFilter extends OncePerRequestFilter {
private static final Logger logger = LoggerFactory.getLogger(JwtAuthenticationFilter.class);
@Autowired
private JwtTokenProvider jwtTokenProvider;
@Autowired
private MyUserDetailsService userDetailsService;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
try {
String jwt = getJwtFromRequest(request);
if (StringUtils.hasText(jwt) && jwtTokenProvider.validateToken(jwt)) {
String username = jwtTokenProvider.getUsernameFromToken(jwt);
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
} catch (Exception ex) {
logger.error("Could not set user authentication in security context", ex);
}
filterChain.doFilter(request, response);
}
private String getJwtFromRequest(HttpServletRequest request) {
String bearerToken = request.getHeader("Authorization");
if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
return bearerToken.substring(7);
}
return null;
}
}
- 配置WebSecurity配置类
在配置SpringSecurity时,我们需要配置一个WebSecurity配置类,代码如下:
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private JwtAuthenticationEntryPoint unauthorizedHandler;
@Autowired
private MyUserDetailsService userDetailsService;
@Bean
public JwtAuthenticationFilter jwtAuthenticationFilter() {
return new JwtAuthenticationFilter();
}
@Override
public void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
authenticationManagerBuilder
.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
}
@Bean(BeanIds.AUTHENTICATION_MANAGER)
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors()
.and()
.csrf()
.disable()
.exceptionHandling()
.authenticationEntryPoint(unauthorizedHandler)
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/api/auth/**")
.permitAll()
.anyRequest()
.authenticated();
http.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
}
}
四、示例1:用户登录
在前端页面中,用户输入用户名和密码,将信息发送到后台,进行认证,并通过JWT生成和返回认证所需的Token信息,下面是示例代码:
function login(username, password) {
return axios.post('/api/auth/login', {
username: username,
password: password
}).then(response => {
if (response.data.token) {
localStorage.setItem('user', JSON.stringify(response.data));
}
return response.data;
});
}
在后端中,我们实现了/login接口,用于接收前端发送的用户名和密码信息,并进行JWT认证和Token的生成,代码如下:
@RestController
@RequestMapping("/api/auth")
public class AuthController {
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private JwtTokenProvider jwtTokenProvider;
@Autowired
private UserRepository userRepository;
@PostMapping("/login")
public ResponseEntity<?> authenticateUser(@Valid @RequestBody LoginRequest loginRequest) {
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
loginRequest.getUsername(),
loginRequest.getPassword()
)
);
SecurityContextHolder.getContext().setAuthentication(authentication);
String jwt = jwtTokenProvider.generateToken(authentication);
return ResponseEntity.ok(new JwtAuthenticationResponse(jwt));
}
}
五、示例2:使用Token进行用户认证
在前端发起请求时,在Http请求头中添加Authorization字段,值为“Bearer ”和Token内容构成的字符串,用于在后台进行认证。示例代码如下:
function getUserInfo() {
const user = JSON.parse(localStorage.getItem('user'));
if (user && user.token) {
return axios.get('/api/user/me', {
headers: { 'Authorization': 'Bearer ' + user.token }
}).then(response => {
return response.data;
});
}
return null;
}
在后端中,我们实现了一个/me接口,用于获取当前用户的信息,请参考示例代码:
@RestController
@RequestMapping("/api/user")
public class UserController {
@Autowired
private UserRepository userRepository;
@GetMapping("/me")
@PreAuthorize("hasRole('USER')")
public UserSummary getCurrentUser(@CurrentUser UserPrincipal currentUser) {
UserSummary userSummary = new UserSummary(currentUser.getId(), currentUser.getUsername(), currentUser.getEmail());
return userSummary;
}
}
六、总结
到这里,我们已经讲解了“SpringBoot整合SpringSecurity实现JWT认证的项目实践”的完整流程,并提供了两个示例用于帮助读者理解如何在实际项目中应用JWT认证。相信读者已经掌握了相关的技能,可以在自己的项目中应用JWT认证。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:SpringBoot整合SpringSecurity实现JWT认证的项目实践 - Python技术站