前言
在企业级开辟或者我们本身的课程设计中,确保用户数据的安全性和访问控制非常紧张。而Spring Security和JWT是都两个强大的工具,它俩结合可以资助我们实现这一目标。
Spring Security提供了全面的安全功能,而JWT则是一种用于身份验证的令牌机制。
JWT简朴介绍
前面两个章节介绍过了Spring Security,这里就不再赘述了!!!
JWT是一种轻量级的身份验证和授权机制,通过发送包罗用户信息的加密令牌来实现身份验证。这个工具我们在前面的文章中也提起过。
整合步调与代码实现
目前大部门项目,大多数是使用前后端分离的模式。前后端分离的环境下,我们使用SpringSecurity解决权限题目的最常见的方案就是SpringSecurity+JWT 。
添加依赖
首先,我们需要在项目标pom.xml文件中添加Spring Security和JWT的依赖:
- <!--JWT-->
- <dependency>
- <groupId>com.auth0</groupId>
- <artifactId>java-jwt</artifactId>
- <version>3.8.1</version>
- </dependency>
- <dependency>
- <groupId>io.jsonwebtoken</groupId>
- <artifactId>jjwt</artifactId>
- <version>0.9.1</version>
- </dependency>
- <!--工具包-->
- <dependency>
- <groupId>cn.hutool</groupId>
- <artifactId>hutool-all</artifactId>
- <version>5.8.0.M3</version>
- </dependency>
- <dependency>
- <groupId>javax.xml.bind</groupId>
- <artifactId>jaxb-api</artifactId>
- </dependency>
- <dependency>
- <groupId>com.alibaba</groupId>
- <artifactId>fastjson</artifactId>
- <version>1.2.75</version>
- </dependency>
复制代码 接下来配置Spring Security,在Spring Security配置类中,我们自定义用户详情服务和认证管理器,并配置HTTP安全策略:
- @Configuration
- @EnableWebSecurity
- public class SecurityConfig extends WebSecurityConfigurerAdapter {
-
- @Autowired
- private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
-
- @Autowired
- private JwtRequestFilter jwtRequestFilter;
-
- @Autowired
- public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
- auth.userDetailsService(userDetailsService()).passwordEncoder(passwordEncoder());
- }
-
- @Bean
- @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
- .csrf().disable()
- .authorizeRequests()
- .antMatchers("/authenticate").permitAll()
- .anyRequest().authenticated()
- .and()
- .exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint)
- .and()
- .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
-
- http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
- }
- }
复制代码 实现JWT天生和验证,我们创建一个JWT工具类,用于天生和剖析JWT:
- java
- @Component
- public class JwtTokenUtil {
-
- private String secret = "your_secret_key"; // 私钥,用于签名JWT
-
- public String generateToken(UserDetails userDetails) {
- Map<String, Object> claims = new HashMap<>();
- return Jwts.builder()
- .setClaims(claims)
- .setSubject(((User) userDetails).getUsername())
- .setIssuedAt(new Date(System.currentTimeMillis()))
- .setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 10)) // 10小时过期
- .signWith(SignatureAlgorithm.HS512, secret)
- .compact();
- }
-
- public String getUsernameFromToken(String token) {
- return getClaimFromToken(token, Claims::getSubject);
- }
-
- public Date getExpirationDateFromToken(String token) {
- return getClaimFromToken(token, Claims::getExpiration);
- }
-
- private <T> T getClaimFromToken(String token, Function<Claims, T> claimsResolver) {
- final Claims claims = getAllClaimsFromToken(token);
- return claimsResolver.apply(claims);
- }
-
- private Claims getAllClaimsFromToken(String token) {
- return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
- }
-
- public boolean validateToken(String token, UserDetails userDetails) {
- final String username = getUsernameFromToken(token);
- return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
- }
-
- private boolean isTokenExpired(String token) {
- final Date expiration = getExpirationDateFromToken(token);
- return expiration.before(new Date());
复制代码
创建JWT过滤器与认证管理器
为了在用户每次请求时验证JWT,我们需要创建一个自定义的过滤器。同时,我们还需要一个认证管理器来处理用户的登录请求。
我们实现JWT过滤器
- @Component
- public class JwtRequestFilter extends OncePerRequestFilter {
-
- @Autowired
- private JwtTokenUtil jwtTokenUtil;
-
- @Autowired
- private UserDetailsService userDetailsService;
-
- @Override
- protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
- throws ServletException, IOException {
- final String requestTokenHeader = request.getHeader("Authorization");
-
- String username = null;
- String jwtToken = null;
- if (requestTokenHeader != null && requestTokenHeader.startsWith("Bearer ")) {
- jwtToken = requestTokenHeader.substring(7);
- try {
- username = jwtTokenUtil.getUsernameFromToken(jwtToken);
- } catch (Exception e) {
- logger.error("Unable to get JWT Token");
- }
- }
-
- if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
- UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
-
- if (jwtTokenUtil.validateToken(jwtToken, userDetails)) {
- UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =
- new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
- usernamePasswordAuthenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
- SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
- }
- }
-
- filterChain.doFilter(request, response);
- }
- }
复制代码 认证管理器,我们创建一个AuthenticationManager的实现来处理用户的登录请求:
- @Service
- public class CustomAuthenticationManager implements AuthenticationManager {
-
- @Autowired
- private UserDetailsService userDetailsService;
-
- @Autowired
- private PasswordEncoder passwordEncoder;
-
- @Override
- public Authentication authenticate(Authentication authentication) throws AuthenticationException {
- String username = authentication.getName();
- String password = authentication.getCredentials().toString();
-
- UserDetails userDetails = userDetailsService.loadUserByUsername(username);
-
- if (userDetails == null) {
- throw new BadCredentialsException("User not found");
- }
-
- if (!passwordEncoder.matches(password, userDetails.getPassword())) {
- throw new BadCredentialsException("Wrong password");
- }
-
- return new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
- }
- }
复制代码 创建控制层LoginController
- RestController
- @RequestMapping("/security")
- public class AuthenticationController {
-
- @Autowired
- private CustomAuthenticationManager authenticationManager;
-
- @Autowired
- private JwtTokenUtil jwtTokenUtil;
-
- @Autowired
- private UserDetailsService userDetailsService;
-
- @PostMapping("/login")
- public ResponseEntity<?> createAuthenticationToken(@Valid @RequestBody LoginRequest loginRequest) throws Exception {
- authenticate(loginRequest.getUsername(), loginRequest.getPassword());
- final UserDetails userDetails = userDetailsService.loadUserByUsername(loginRequest.getUsername());
- final String token = jwtTokenUtil.generateToken(userDetails);
- return ResponseEntity.ok(new JwtAuthenticationResponse(token));
- }
-
- private void authenticate(String username, String password) throws Exception {
- try {
- authenticationManager.authenticate(
- new UsernamePasswordAuthenticationToken(username, password)
- );
- } catch (DisabledException e) {
- throw new Exception("USER_DISABLED", e);
- } catch (BadCredentialsException e) {
- throw new Exception("INVALID_CREDENTIALS", e);
- }
- }
- }
复制代码 使用ApiFox测试
这样,我们就可以构建一个安全且高效的Web应用了。
小结
Spring Security提供了强大的身份验证和授权功能,而JWT则提供了一种轻量级的令牌机制来验证用户身份。通过结合使用,我们可以实现无缝的用户身份验证和访问控制,然后掩护我们应用的数据安全。
文章到这里就先结束了,后续会继续分享相关的知识点。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |