Spring-Security-JWT-初探

SpringDemo 是 Spring boot + Spring Security + JWT 整合的项目。
参考 securing-spring-boot-with-jwts.

Spring Security 框架

依赖:

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-security</artifactId>
</dependency>

主要功能: 提供 链式过滤器(FilterChain) 支持。

  1. 定义属性类 WebSecurityConfig,继承于 WebSecurityConfigurerAdapter.

  2. 重载 configure(HttpSecurity http) 方法,设置拦截的 url 和 filters.

    到这里可以利用 curl 或者 浏览器 查看效果,访问 /users 路径会跳转到 /login。

JWT 支持

依赖:

<dependency>
	<groupId>io.jsonwebtoken</groupId>
	<artifactId>jjwt</artifactId>
	<version>0.9.1</version>
</dependency>

主要功能:提供 JWT 的生成,验证(用户名,过期时间)。
以下是几个主要的类, 这几个类交互的过程(泳道图):

/login:
  JwtLoginFilter          |     AuthenticationManager |   TokenAuthenticationProvider  | JWTAuthenticationService 
  获取username&password ---提交验证-->   调用AuthenticationProvider  --->  验证username&password
																|  验证成功		
  successfulAuthentication    <--------- 返回   <-------------    生成权限/角色
			-----------------------------------------------------------------------------------------------> 生成 JWT,写入HttpServletResponse
																|  验证失败
  unsuccessfulAuthentication  <--------- 返回	<---------------   BadCredentialsException 异常 
			异常信息写入 HttpServletResponse

  1. JWTAuthenticationService
    功能:
  • String generateJwt(String username)

    生成 JWT,将 username 放入 subject 中,并 claims : Authorization: ADMIN, AUTH_WRITE.

  • void addAuthentication(HttpServletResponse response,String username)

    生成 JWT, 并将 JWT 写入到 response 中.

  • Claims extranctClaims(String token) throws ExpiredJwtException

    将 token 转换成 JWT claims. 注:过期的 JWT 会抛出 ExpiredJwtException 异常.

  • Authentication getAuthentication(HttpServletRequest request) throws ExpiredJwtException

    从 HttpServletRequest 中获取 header 中的 Authorization 域,将其转换成 Claims, 从中获取 GrantedAuthorities, 交由 UsernamePasswordAuthenticationToken 封装成 Authentication.

    注:过期的 JWT 会抛出 ExpiredJwtException 异常。

  1. TokenAuthenticationProvider
    功能: 验证 未验证的 Authentication,授权权限/角色。
    继承于 AuthenticationProvider 接口,实现 authenticate 方法.
  • Authentication authenticate(Authentication authentication) throws AuthenticationException

    从 Authentiation 中获取 username, password, 验证是否和 「数据库」 中的相同(这里只是直接写死的)。
    然后为 创建权限/角色,利用 UsernamePasswordAuthenticationToken 封装成 Authentication, 返回授权。

  1. JwtLoginFilter
    继承抽象类 AbstractAuthenticationProcessingFilter,需要实现 attempAuthentication 方法。
  • Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)

    首先从 HttpServletRequest 中获取 输入流内容,将 内容 转换成 AccountCrediential 类(封装了 username, password).
    如果输入流内容为空,在 内部 catch 了异常 MismatchedInputException, 直接向 HttpServletResponse 中写入 错误信息。

    然后利用 UsernamePasswordAuthenticationToken 封装 username, password , 交由 AuthenticationManager 验证。

    AuthenticationManager 使用 WebSecurityConfig 中定义的 TokenAuthenticationProvider 验证 username 和 password .

    AuthenticationManager 验证成功后,调用 successfulAuthentication 回调,里面包含的 Authentication 是 Authenticated = true 的,在 successfulAuthentication 中调用 JWTAuthenticationService.addAuthentication 生成 JWT,并写入到 HttpServletResponse 中.

    AuthenticationManager 验证失败后,调用 unsuccessfulAuthentication 回调,里面包含 AuthenticationException 异常,直接将 异常信息 写入到 HttpServletResponse 中.

  • void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
    Authentication authResult)

    验证成功回调.

  • void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
    AuthenticationException failed)

    验证失败回调.

  1. JwtAuthenticationFilter
    继承 GenericFilterBean ,实现 doFilter 方法.
  • doFilter(ServletRequest request, ServletResponse response, FilterChain chain)

    从 HttpServletRequest 中获取 JWT Authentication, 如果 Authentication 不为空,则将 Authentication 设置到
    SecurityContextHolder 中;
    否则直接向 HttpServletResponse 中写入 JWT 已经过期的提示信息。

测试:

curl -H "Content-Type: application/json" -X POST localhost:8080/login -d '
{
	"username": "admin",
	"password": "123456"
}'
# 返回数据,将 data 中的 token 复制
curl -H "Content-Type: application/json" -H "Authorization: Bearer <JWT token>" -X GET localhost:8080/users
curl -H "Content-Type: application/json" -H "Authorization: Bearer <JWT token>" -X GET localhost:8080/user/GT

lombok

依赖:

<dependency>
	<groupId>org.projectlombok</groupId>
	<artifactId>lombok</artifactId>
	<optional>true</optional>
</dependency>

主要功能:简化 Bean 的不必要的编写。
注: 需要在 IDE 中添加 lombok 插件。在官网上下载 lombok.jar 后,利用

java -jar lombok.jar

运行,选择需要添加插件的 IDE (eclipse, IDEA),完成安装, 重新启动 IDE。

Mybatis-generator

这是一款可以自动生成 mapper.xml, model 以及 dao 的自动化插件。
需要在 pom.xml 中引入 mybatis-generator 的依赖,除此之外,还需要在 IDE 中安装 mybatis-generator 的插件。

<dependencies>
	<dependency>
			<groupId>org.mybatis.generator</groupId>
			<artifactId>mybatis-generator-core</artifactId>
			<version>1.3.5</version>
			<scope>provided</scope>
	</dependency>
</dependencies>
<build>
	<plugins>
			<!-- mvn mybatis-generator:generate -->
			<plugin>
				<groupId>org.mybatis.generator</groupId>
				<artifactId>mybatis-generator-maven-plugin</artifactId>
				<version>1.3.5</version>
				
				<configuration>
					<!-- generatorConfiguration.xml -->
					<configurationFile>src/main/resources/config/mybatis-generator.xml</configurationFile>
					<overwrite>true</overwrite>
				</configuration>
				
				<dependencies>
					<dependency>
						<groupId>org.mariadb.jdbc</groupId>
						<artifactId>mariadb-java-client</artifactId>
						<version>2.5.0</version>
					</dependency>
					<dependency>
						<groupId>org.mybatis.generator</groupId>
						<artifactId>mybatis-generator-core</artifactId>
						<version>1.3.5</version>
					</dependency>
				</dependencies>
			</plugin>
	</plugins>
</build>

在 Eclipse 中的 mybatis-generator 插件直接在 marketplace 中查找就有,安装后重启 IDE。

然后,编写 mybatis-generator 的配置文件,默认为 src/main/resources/generatorConfig.xml(其实在哪,叫啥无所谓)。
这个项目里放在了 src/main/resources/config/mybatis-generator.xml, 代码就不粘贴了。按照
* jdbcConnection: JDBC 配置
* javaModelGenerator: 指定自动生成的 POJO 置于哪个包下
* sqlMapGenerator: 指定自动生成的 mapper.xml 置于哪个包下
* javaClientGenerator: 指定自动生成的 DAO 接口置于哪个包下
* table: 指定数据表名,可以使用 _ 和 % 通配符
配置。

配置好后,可以使用 mvn mybatis-generator:generate 生成,也可以使用 Eclipse 中的工具: 选中 mybatis-generator.xml -> 右键 -> Run As -> Run Mybatis Generator.
运行就可以生成 mapper.xml, model 以及 dao.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Boot和Spring Security是非常常用的Java框架和库,用于构建安全和可扩展的Web应用程序。JWT(JSON Web Token)是一种用于在网络应用间安全传递身份验证和声明信息的标准。 要在Spring Boot中使用Spring Security和JWT,你需要进行以下步骤: 1. 添加依赖:在你的项目的pom.xml文件中添加Spring Security和JWT的依赖。 ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.1</version> </dependency> ``` 2. 创建JWT工具类:编写一个JWT工具类,用于生成和解析JWT。 ```java import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import org.springframework.security.core.userdetails.UserDetails; import java.util.Date; import java.util.HashMap; import java.util.Map; public class JwtUtil { private static final String SECRET_KEY = "your-secret-key"; private static final long EXPIRATION_TIME = 864_000_000; // 10 days public static String generateToken(UserDetails userDetails) { Map<String, Object> claims = new HashMap<>(); return Jwts.builder() .setClaims(claims) .setSubject(userDetails.getUsername()) .setIssuedAt(new Date()) .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME)) .signWith(SignatureAlgorithm.HS512, SECRET_KEY) .compact(); } public static String extractUsername(String token) { return extractClaims(token).getSubject(); } public static Date extractExpiration(String token) { return extractClaims(token).getExpiration(); } private static Claims extractClaims(String token) { return Jwts.parser() .setSigningKey(SECRET_KEY) .parseClaimsJws(token) .getBody(); } } ``` 3. 配置Spring Security:创建一个继承自WebSecurityConfigurerAdapter的配置类,用于配置Spring Security。 ```java import org.springframework.beans.factory.annotation.Autowired; 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.method.configuration.EnableGlobalMethodSecurity; 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.core.userdetails.UserDetailsService; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.crypto.password.Pbkdf2PasswordEncoder; @Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private UserDetailsService userDetailsService; @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder()); } @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 Pbkdf2PasswordEncoder(); } @Override @Bean public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } } ``` 4. 创建UserDetails实现类:实现Spring Security的UserDetails接口,用于获取用户信息。 ```java import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import java.util.Collection; import java.util.List; public class CustomUserDetails implements UserDetails { private String username; private String password; public CustomUserDetails(String username, String password) { this.username = username; this.password = password; } @Override public Collection<? extends GrantedAuthority> getAuthorities() { return List.of(() -> "ROLE_USER"); } @Override public String getPassword() { return password; } @Override public String getUsername() { return username; } // Other UserDetails methods... @Override public boolean isAccountNonExpired() { return true; } @Override public boolean isAccountNonLocked() { return true; } @Override public boolean isCredentialsNonExpired() { return true; } @Override public boolean isEnabled() { return true; } } ``` 5. 创建认证和授权的Controller:创建一个RestController,用于处理用户认证和授权的请求。 ```java 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.userdetails.UserDetails; import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/api/auth") public class AuthController { @Autowired private AuthenticationManager authenticationManager; @Autowired private UserDetailsService userDetailsService; @PostMapping("/login") public ResponseEntity<String> login(@RequestBody AuthRequest authRequest) { authenticationManager.authenticate( new UsernamePasswordAuthenticationToken(authRequest.getUsername(), authRequest.getPassword()) ); UserDetails userDetails = userDetailsService.loadUserByUsername(authRequest.getUsername()); String token = JwtUtil.generateToken(userDetails); return ResponseEntity.ok(token); } } class AuthRequest { private String username; private String password; // getters and setters... } ``` 这样,你就可以在Spring Boot应用程序中使用Spring Security和JWT来实现认证和授权了。当用户登录时,会生成一个JWT,并在以后的请求中使用该JWT进行身份验证。 请注意,以上代码只是一个简单的示例,你可能需要根据你的实际需求进行适当的修改和扩展。另外,确保保护敏感信息(如密钥)的安全。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值