Spring Boot Security OAuth2 JWT完整示例
时间: 2024-02-22 10:47:19 浏览: 155
Spring Boot Security是一个非常流行的安全框架,可以帮助开发人员实现各种安全功能。其中,OAuth2和JWT是两个非常重要的安全技术,可以用于实现授权和认证。下面是一个基于Spring Boot Security的完整示例,演示如何使用OAuth2和JWT实现安全功能。
1. 创建一个Spring Boot项目
使用Spring Initializr创建一个新的Spring Boot项目,添加以下依赖:
- Spring Web
- Spring Security
- Spring Data JPA
- H2 Database
- Spring Security OAuth2
- jjwt
2. 添加配置文件
在application.properties文件中添加以下配置:
```
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.jpa.hibernate.ddl-auto=create-drop
spring.h2.console.enabled=true
spring.h2.console.path=/h2-console
spring.security.oauth2.client.registration.myapp.client-id=myapp
spring.security.oauth2.client.registration.myapp.client-secret=myappsecret
spring.security.oauth2.client.registration.myapp.scope=read,write
spring.security.oauth2.client.provider.myapp.authorization-uri=http://localhost:8080/oauth/authorize
spring.security.oauth2.client.provider.myapp.token-uri=http://localhost:8080/oauth/token
spring.security.oauth2.client.provider.myapp.user-info-uri=http://localhost:8080/userinfo
spring.security.oauth2.client.provider.myapp.user-name-attribute=name
```
这些配置将用于配置数据源、Hibernate、H2控制台和OAuth2。
3. 创建实体类和仓库
创建一个User实体类,用于表示用户信息:
```java
@Entity
@Table(name = "user")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, unique = true)
private String username;
@Column(nullable = false)
private String password;
@Column(nullable = false)
private String email;
// getters and setters
}
```
创建一个UserRepository接口,用于操作User实体类:
```java
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByUsername(String username);
}
```
4. 创建用户服务
创建一个UserService接口,用于定义获取用户和创建用户的方法:
```java
public interface UserService {
User createUser(String username, String password, String email);
Optional<User> getUserByUsername(String username);
}
```
创建一个UserServiceImpl实现UserService接口,用于实现上述方法:
```java
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserRepository userRepository;
@Autowired
private PasswordEncoder passwordEncoder;
@Override
public User createUser(String username, String password, String email) {
User user = new User();
user.setUsername(username);
user.setPassword(passwordEncoder.encode(password));
user.setEmail(email);
return userRepository.save(user);
}
@Override
public Optional<User> getUserByUsername(String username) {
return userRepository.findByUsername(username);
}
}
```
5. 配置Spring Security
创建一个WebSecurityConfig类,用于配置Spring Security:
```java
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserService userService;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/oauth/**").permitAll()
.anyRequest().authenticated()
.and()
.csrf().disable()
.formLogin().disable()
.httpBasic().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(username -> userService.getUserByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException("User not found: " + username)))
.passwordEncoder(passwordEncoder());
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
```
这个配置类将所有请求都需要进行认证,但是允许OAuth2请求。同时,禁用了CSRF、表单登录、HTTP基本认证和会话管理。
6. 配置OAuth2
创建一个OAuth2Config类,用于配置OAuth2:
```java
@Configuration
@EnableAuthorizationServer
public class OAuth2Config extends AuthorizationServerConfigurerAdapter {
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private UserService userService;
@Autowired
private PasswordEncoder passwordEncoder;
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("myapp")
.secret(passwordEncoder.encode("myappsecret"))
.authorizedGrantTypes("authorization_code", "refresh_token")
.scopes("read", "write")
.redirectUris("http://localhost:8081/login/oauth2/code/myapp");
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager)
.userDetailsService(username -> userService.getUserByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException("User not found: " + username)))
.accessTokenConverter(accessTokenConverter())
.pathMapping("/oauth/token", "/signin");
}
@Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey("secret");
return converter;
}
@Bean
public TokenStore tokenStore() {
return new JwtTokenStore(accessTokenConverter());
}
}
```
这个配置类定义了一个OAuth2客户端,将授权码和刷新令牌作为授权类型,并允许读取和写入作用域。同时,定义了一个端点配置,将认证管理器、用户详情服务、访问令牌转换器和令牌存储配置到端点上。
7. 创建控制器
创建一个UserController类,用于处理用户相关的请求:
```java
@RestController
@RequestMapping("/users")
public class UserController {
@Autowired
private UserService userService;
@PostMapping
public User createUser(@RequestBody UserDto userDto) {
return userService.createUser(userDto.getUsername(), userDto.getPassword(), userDto.getEmail());
}
@GetMapping("/{username}")
public User getUser(@PathVariable String username) {
return userService.getUserByUsername(username)
.orElseThrow(() -> new NotFoundException("User not found: " + username));
}
}
```
这个控制器中定义了创建用户和获取用户的方法。
8. 创建JWT过滤器
创建一个JwtFilter类,用于在请求中验证JWT令牌:
```java
public class JwtFilter extends OncePerRequestFilter {
private static final String AUTHORIZATION_HEADER = "Authorization";
@Autowired
private JwtAccessTokenConverter accessTokenConverter;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String authorizationHeader = request.getHeader(AUTHORIZATION_HEADER);
if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
String token = authorizationHeader.substring(7);
Authentication authentication = accessTokenConverter.extractAuthentication(token);
SecurityContextHolder.getContext().setAuthentication(authentication);
}
filterChain.doFilter(request, response);
}
}
```
这个过滤器会从请求头中获取JWT令牌,并使用令牌转换器验证令牌。如果验证通过,则将用户认证信息存储到Spring Security上下文中。
9. 注册JWT过滤器
在WebSecurityConfig类中,添加以下配置:
```java
@Bean
public JwtFilter jwtFilter() {
return new JwtFilter();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterBefore(jwtFilter(), UsernamePasswordAuthenticationFilter.class);
// ...
}
```
这个配置将JwtFilter注册到Spring Security过滤器链中。
10. 创建JWT工具类
创建一个JwtUtils类,用于生成和解析JWT令牌:
```java
public class JwtUtils {
public static final String SUBJECT = "myapp";
public static String generateToken(User user) {
Map<String, Object> claims = new HashMap<>();
claims.put("sub", SUBJECT);
claims.put("id", user.getId());
claims.put("username", user.getUsername());
claims.put("email", user.getEmail());
Date now = new Date();
Date expiration = new Date(now.getTime() + 60 * 60 * 1000);
return Jwts.builder()
.setClaims(claims)
.setIssuedAt(now)
.setExpiration(expiration)
.signWith(SignatureAlgorithm.HS256, "secret")
.compact();
}
public static Long getUserIdFromToken(String token) {
Claims claims = Jwts.parser()
.setSigningKey("secret")
.parseClaimsJws(token)
.getBody();
return claims.get("id", Long.class);
}
}
```
这个工具类将用户信息存储到JWT令牌中,并使用HS256算法进行签名。
11. 创建DTO类
创建一个UserDto类,用于接收创建用户的请求:
```java
public class UserDto {
private String username;
private String password;
private String email;
// getters and setters
}
```
12. 测试
启动应用程序,并使用以下命令创建一个用户:
```
curl -X POST -H 'Content-Type: application/json' -d '{"username":"test","password":"test123","email":"test@example.com"}' http://localhost:8080/users
```
使用以下命令获取访问令牌:
```
curl -X POST -vu myapp:myappsecret http://localhost:8080/oauth/token -H 'Accept: application/json' -d 'grant_type=authorization_code&code={CODE}&redirect_uri=http://localhost:8081/login/oauth2/code/myapp'
```
将{CODE}替换为授权码,并将返回的访问令牌用于获取用户信息:
```
curl -H 'Authorization: Bearer {ACCESS_TOKEN}' http://localhost:8080/users/test
```
将{ACCESS_TOKEN}替换为访问令牌,在请求头中发送即可。
以上就是一个完整的Spring Boot Security OAuth2 JWT示例,演示了如何实现授权和认证。
阅读全文