您好,登录后才能下订单哦!
在现代的Web应用中,系统的安全性和用户的权限管理是非常重要的。Spring Boot 快速开发框架,结合 Spring Security 和 JWT(JSON Web Token)可以很好地实现系统的认证与授权。本文将详细介绍如何使用 Spring Boot、Spring Security 和 JWT 来实现系统的认证与授权。
Spring Boot 是 Spring 框架的一个子项目,旨在简化 Spring 应用的初始搭建和开发过程。它通过自动配置和约定优于配置的原则,使得开发者可以快速启动和运行 Spring 应用。
java -jar
命令运行。Spring Security 是一个功能强大且高度可定制的身份验证和访问控制框架。它是 Spring 生态系统中的一个重要组成部分,用于保护基于 Spring 的应用程序。
JWT(JSON Web Token)是一种开放标准(RFC 7519),用于在各方之间安全地传输信息作为 JSON 对象。JWT 通常用于身份验证和信息交换。
JWT 由三部分组成:
首先,我们需要创建一个 Spring Boot 项目。可以使用 Spring Initializr 来快速生成项目。
项目结构如下:
src
├── main
│ ├── java
│ │ └── com
│ │ └── example
│ │ └── demo
│ │ ├── config
│ │ ├── controller
│ │ ├── model
│ │ ├── repository
│ │ ├── security
│ │ └── service
│ └── resources
│ ├── application.properties
│ └── static
└── test
└── java
└── com
└── example
└── demo
在 src/main/resources/application.properties
文件中添加以下配置:
# Server port
server.port=8080
# H2 Database configuration
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password
spring.h2.console.enabled=true
# JWT configuration
jwt.secret=mySecretKey
jwt.expiration=86400000
在 src/main/java/com/example/demo/config
目录下创建 SecurityConfig
类,用于配置 Spring Security。
package com.example.demo.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/api/auth/**").permitAll()
.anyRequest().authenticated()
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
在 SecurityConfig
类中,我们还需要配置用户认证。可以通过内存中的用户或数据库中的用户来进行认证。
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("user").password(passwordEncoder().encode("password")).roles("USER")
.and()
.withUser("admin").password(passwordEncoder().encode("admin")).roles("ADMIN");
}
如果使用数据库中的用户进行认证,需要创建 UserDetailsService
的实现类。
package com.example.demo.security;
import com.example.demo.repository.UserRepository;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
@Service
public class CustomUserDetailsService implements UserDetailsService {
private final UserRepository userRepository;
public CustomUserDetailsService(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return userRepository.findByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException("User not found with username: " + username));
}
}
然后在 SecurityConfig
类中配置 CustomUserDetailsService
:
@Autowired
private CustomUserDetailsService userDetailsService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
在 src/main/java/com/example/demo/security
目录下创建 JwtTokenProvider
类,用于生成和验证 JWT。
package com.example.demo.security;
import io.jsonwebtoken.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component;
import java.util.Date;
@Component
public class JwtTokenProvider {
@Value("${jwt.secret}")
private String jwtSecret;
@Value("${jwt.expiration}")
private int jwtExpirationInMs;
public String generateToken(Authentication authentication) {
UserPrincipal userPrincipal = (UserPrincipal) authentication.getPrincipal();
Date now = new Date();
Date expiryDate = new Date(now.getTime() + jwtExpirationInMs);
return Jwts.builder()
.setSubject(Long.toString(userPrincipal.getId()))
.setIssuedAt(new Date())
.setExpiration(expiryDate)
.signWith(SignatureAlgorithm.HS512, jwtSecret)
.compact();
}
public Long getUserIdFromJWT(String token) {
Claims claims = Jwts.parser()
.setSigningKey(jwtSecret)
.parseClaimsJws(token)
.getBody();
return Long.parseLong(claims.getSubject());
}
public boolean validateToken(String authToken) {
try {
Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(authToken);
return true;
} catch (SignatureException ex) {
// Invalid JWT signature
} catch (MalformedJwtException ex) {
// Invalid JWT token
} catch (ExpiredJwtException ex) {
// Expired JWT token
} catch (UnsupportedJwtException ex) {
// Unsupported JWT token
} catch (IllegalArgumentException ex) {
// JWT claims string is empty
}
return false;
}
}
在 src/main/java/com/example/demo/security
目录下创建 JwtAuthenticationFilter
类,用于在请求中提取 JWT 并进行验证。
package com.example.demo.security;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class JwtAuthenticationFilter extends OncePerRequestFilter {
private final JwtTokenProvider tokenProvider;
private final CustomUserDetailsService customUserDetailsService;
public JwtAuthenticationFilter(JwtTokenProvider tokenProvider, CustomUserDetailsService customUserDetailsService) {
this.tokenProvider = tokenProvider;
this.customUserDetailsService = customUserDetailsService;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
try {
String jwt = getJwtFromRequest(request);
if (StringUtils.hasText(jwt) && tokenProvider.validateToken(jwt)) {
Long userId = tokenProvider.getUserIdFromJWT(jwt);
UserDetails userDetails = customUserDetailsService.loadUserById(userId);
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;
}
}
在 SecurityConfig
类中配置 JwtAuthenticationFilter
:
@Autowired
private JwtTokenProvider tokenProvider;
@Autowired
private CustomUserDetailsService customUserDetailsService;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/api/auth/**").permitAll()
.anyRequest().authenticated()
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.addFilterBefore(new JwtAuthenticationFilter(tokenProvider, customUserDetailsService), UsernamePasswordAuthenticationFilter.class);
}
在 src/main/java/com/example/demo/model
目录下创建 User
类,用于表示用户实体。
package com.example.demo.model;
import javax.persistence.*;
import java.util.Collection;
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String username;
private String password;
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(
name = "users_roles",
joinColumns = @JoinColumn(name = "user_id", referencedColumnName = "id"),
inverseJoinColumns = @JoinColumn(name = "role_id", referencedColumnName = "id")
)
private Collection<Role> roles;
// Getters and Setters
}
在 src/main/java/com/example/demo/model
目录下创建 Role
类,用于表示角色实体。
package com.example.demo.model;
import javax.persistence.*;
import java.util.Collection;
@Entity
public class Role {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@ManyToMany(mappedBy = "roles")
private Collection<User> users;
// Getters and Setters
}
在 src/main/java/com/example/demo/repository
目录下创建 UserRepository
接口,用于操作用户数据。
package com.example.demo.repository;
import com.example.demo.model.User;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.Optional;
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByUsername(String username);
}
在 src/main/java/com/example/demo/service
目录下创建 UserService
类,用于处理用户相关的业务逻辑。
package com.example.demo.service;
import com.example.demo.model.User;
import com.example.demo.repository.UserRepository;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
@Service
public class UserService {
private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;
public UserService(UserRepository userRepository, PasswordEncoder passwordEncoder) {
this.userRepository = userRepository;
this.passwordEncoder = passwordEncoder;
}
public User registerUser(User user) {
user.setPassword(passwordEncoder.encode(user.getPassword()));
return userRepository.save(user);
}
}
在 src/main/java/com/example/demo/controller
目录下创建 AuthController
类,用于处理用户认证相关的请求。
package com.example.demo.controller;
import com.example.demo.model.User;
import com.example.demo.payload.JwtAuthenticationResponse;
import com.example.demo.payload.LoginRequest;
import com.example.demo.payload.SignUpRequest;
import com.example.demo.security.JwtTokenProvider;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api/auth")
public class AuthController {
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private JwtTokenProvider tokenProvider;
@Autowired
private UserService userService;
@PostMapping("/signin")
public ResponseEntity<?> authenticateUser(@RequestBody LoginRequest loginRequest) {
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
loginRequest.getUsername(),
loginRequest.getPassword()
)
);
SecurityContextHolder.getContext().setAuthentication(authentication);
String jwt = tokenProvider.generateToken(authentication);
return ResponseEntity.ok(new JwtAuthenticationResponse(jwt));
}
@PostMapping("/signup")
public ResponseEntity<?> registerUser(@RequestBody SignUpRequest signUpRequest) {
User user = new User();
user.setUsername(signUpRequest.getUsername());
user.setPassword(signUpRequest.getPassword());
User result = userService.registerUser(user);
return ResponseEntity.ok(result);
}
}
在 src/main/java/com/example/demo/payload
目录下创建 LoginRequest
、SignUpRequest
和 JwtAuthenticationResponse
类,用于表示认证请求和响应。
package com.example.demo.payload;
public class LoginRequest {
private String username;
private String password;
// Getters and Setters
}
package com.example.demo.payload;
public class SignUpRequest {
private String username;
private String password;
// Getters and Setters
}
package com.example.demo.payload;
public class JwtAuthenticationResponse {
private String accessToken;
public JwtAuthenticationResponse(String accessToken) {
this.accessToken = accessToken;
}
// Getter
}
在 src/main/java/com/example/demo/repository
目录下创建 RoleRepository
接口,用于操作角色数据。
package com.example.demo.repository;
import com.example.demo.model.Role;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.Optional;
public interface RoleRepository extends JpaRepository<Role, Long> {
Optional<Role> findByName(String name);
}
在 src/main/java/com/example/demo/service
目录下创建 RoleService
类,用于处理角色相关的业务逻辑。
”`java package com.example.demo.service;
import com.example.demo.model.Role; import com.example.demo.repository.RoleRepository; import org.springframework.stereotype.Service;
@Service public class RoleService {
private final RoleRepository roleRepository;
public RoleService(RoleRepository roleRepository) {
this
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。