目录
- 1. 整合spring security
- 1. 导入依赖
- 2. 配置类
- 3. 实体类实现UserDetails接口
- 4. 业务逻辑实现类实现UserDetailsService接口
- 5. 控制类实现登录功能
- 6. 测试登录功能
- 2. 分析源码
- 1. UsernamePasswordAuthenticationToken
- 2. Authentication接口
- 3. SecurityContextHolder类
1. 整合spring security
1. 导入依赖
<!-- security依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
<scope>compile</scope>
</dependency>
<!-- 该模块包含 security 命名空间解析代码和Java配置代码,如需要将XML命名空间进行配置或 Spring Security 的 Java 配置支持 -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
</dependency>
<!-- 该模块包含过滤器和相关的web安全基础设施代码,如servlet API依赖的东西,认证服务和基于URL的访问控制-->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
</dependency>
2. 配置类
@Configuration
public class SecurityConfig{
@Bean //配置加密器
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();// 加密方式
}
/*
resUserBizImpl必须实现UserDetailsService接口
UserDetailsService接口是spring security的用户认证接口
用于从数据库中读取用户信息
*/
private ResUserBizImpl resUserBiz;
@Autowired //这里使用set方法注入,可以避免循环依赖
public void setResUserBiz(ResUserBizImpl resUserBiz) {
this.resUserBiz = resUserBiz;
}
@Bean //认证服务 组装组件
public AuthenticationProvider authenticatorProvider() {DaoAuthenticationProvider provider =
new DaoAuthenticationProvider();
//设置密码加密器
provider.setPasswordEncoder(passwordEncoder());
//设置用户信息获取服务 获取用户信息
provider.setUserDetailsService(resUserBiz);
return provider;
}
@Bean //认证管理器
public AuthenticationManager authenticationManager(AuthenticationConfiguration configuration)
throws Exception {
//获取认证管理器
return configuration.getAuthenticationManager();
}
@Bean //配置安全过滤器链
public SecurityFilterChain securityFilterChain(HttpSecurity http)
throws Exception {
//取消默认的登录页面
http.formLogin(AbstractHttpConfigurer::disable)
//取消默认的登出页面
.logout(AbstractHttpConfigurer::disable)
//将自己的认证服务加入
.authenticationProvider(authenticatorProvider())
//禁用csrf保护
.csrf(AbstractHttpConfigurer::disable)
//禁用session,因为使用token
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
//禁用http基本认证,因为传输数据用的post,且请求体为JSON
.httpBasic(AbstractHttpConfigurer::disable)
//开放接口,除开放的接口外,其他接口都需要认证
.authorizeHttpRequests(request -> request
.requestMatchers(HttpMethod.POST, "/user/login","/user/register","/user/logout").permitAll()
.requestMatchers(HttpMethod.GET, "/captcha/getCaptcha").permitAll()
.anyRequest().authenticated());
return http.build();
}
}
3. 实体类实现UserDetails接口
UserDetails接口是spring security提供的,用于封装用户信息,包括用户名、密码、角色等。通过这个接口,可以将用户的详细信息封装到UserDetails对象中,并通过UserDetailsService接口的实现类来提供用户信息。
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Resuser implements Serializable, UserDetails {
@TableId(type = IdType.AUTO)
private Integer userid;
private String username;
// UserDetails接口中定义的密码字段名必须为password
@TableField(value = "pwd")
private String password;
private String email;
//表中没有该字段 用于封装角色
@TableField(exist = false)
private String role="user";
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
// 返回一个SimpleGrantedAuthority对象,表示用户的角色
return List.of(new SimpleGrantedAuthority(role));
}
@Override
public String getPassword() {
return this.password;
}
@Override
public boolean isAccountNonExpired() {
return true;//表示账号没有过期
}
@Override
public boolean isAccountNonLocked() {
return true;//表示账号没有被锁定
}
@Override
public boolean isCredentialsNonExpired() {
return true;//表示密码没有过期
}
@Override
public boolean isEnabled() {
return true;//表示账号可用
}
}
4. 业务逻辑实现类实现UserDetailsService接口
这里实现了UserDetailsService接口中的loadUserByUsername方法,用于根据用户名查询用户信息。
@Service
@Slf4j
public class ResUserBizImpl implements ResUserBiz, UserDetailsService {
@Autowired
private ResUserMapper resUserMapper;
// 注入加密器
@Autowired
private PasswordEncoder passwordEncoder;
@Override //UserDetailsService接口的方法
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
LambdaQueryWrapper<Resuser> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(Resuser::getUsername,username);
try{
Resuser resuser = resUserMapper.selectOne(queryWrapper);
return resuser;
}catch (Exception e){
log.error("用户不存在");
return null;
}
}
}
5. 控制类实现登录功能
@RestController
@RequestMapping("/user")
public class ResUserController {
@Autowired
private ResUserBizImpl resUserBiz;
@Autowired // 注入认证管理器
private AuthenticationManager authenticationManager;
@Autowired // 注入jwt工具类
private JwtUtil jwtUtil;
@RequestMapping("/login")// /user/login
public ResponseResult login(
@RequestBody // 将请求体中的json数据映射为ResUserVO对象
@Valid // 开启校验,必须有resuserVO对象才能通过校验
ResUserVO user,HttpSession session){
//加入验证码 因为将验证码存在在session中,所以需要从session中取出来
String captcha = (String) session.getAttribute("captcha");
//判断验证码是否正确
if(!captcha.equalsIgnoreCase(user.getCaptcha())){
return ResponseResult.error("验证码错误");
}
//使用authenticate()方法接收UsernamePasswordAuthenticationToken对象,并返回一个包含用户详细信息的 Authentication 对象。
Authentication authentication = authenticationManager.authenticate(
//将用户名和密码[和证书]封装到UsernamePasswordAuthenticationToken对象中,用来包装认证信息
new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPwd())
);
//getContext()获取当前线程的SecurityContext,并返回一个SecurityContext对象。
//setAuthentication()设置当前线程的SecurityContext,并将Authentication对象作为参数传入。
SecurityContextHolder.getContext().setAuthentication(authentication);
//使用getPrincipal()方法获取认证信息,并转换为UserDetails对象
UserDetails userDetails = (UserDetails) authentication.getPrincipal();
Resuser resuser = (Resuser) userDetails;
//生成JWT负载
Map payload = new HashMap();
payload.put("userid",String.valueOf(resuser.getUserid()));
payload.put("username",userDetails.getUsername());
payload.put("role",resuser.getRole());
payload.put("email",resuser.getEmail());
//生成JWT令牌
String token = jwtUtil.getToken(payload);
//返回token,后面使用token进行认证
return ResponseResult.ok("登录成功").setData(token);
}
}
登录认证流程图:
JWT工具类:
/**
* 提供Jwt工具类
* 提供Token生成和验证方法
*/
@Component //托管spring容器
public class JwtUtil {
// 密钥
private static final String key = "3f2e1d4c5b6a79808f7e6d5c4b3a29181716151413121110";
/**
* 生成JWT令牌
* @param payload
* @return
*/
public String getToken(Map payload){
//设置头部信息
Map headers = new HashMap();
//设置签名算法
headers.put("alg", "HS256");
//设置令牌类型
headers.put("typ", "JWT");
//生成令牌
String jwt = Jwts.builder()
//设置头信息
.setHeaderParams(headers)
//设置负载信息
.setClaims(payload)
//使用HS256算法和密钥对JWT进行签名
.signWith(SignatureAlgorithm.HS256,key)
//将之前设置的头部信息、负载信息和签名信息组合成一个完整的JWT,并以字符串形式返回
.compact();
System.out.println(jwt);
return jwt;
}
/**
* 解析JWT令牌
* @param token
* @return
*/
public Claims parseToken(String token){
try{
System.out.println("开始解析令牌...");
Claims claims = Jwts.parser()//创建一个解析器
.setSigningKey(key)//设置验证签名的密钥
.parseClaimsJws(token)//传入需要进行解析的token并解析
.getBody();//从Jws<Claims>对象中获取负载信息
System.out.println(claims);
return claims;
}catch (Exception e){
e.printStackTrace();
}
return null;
}
}
使用JWT可参考:JWT生成、解析token
6. 测试登录功能
2. 分析源码
1. UsernamePasswordAuthenticationToken
将用户的认证信息(如用户名和密码)封装成一个对象,以便在认证过程中使用。
public class UsernamePasswordAuthenticationToken extends AbstractAuthenticationToken {
private static final long serialVersionUID = 620L;
private final Object principal;
private Object credentials;
/*
常用的构造函数
principal:认证的主体信息,通常为用户名或用户对象。
credentials:认证的凭证信息,通常为密码。
*/
public UsernamePasswordAuthenticationToken(Object principal, Object credentials) {
super((Collection)null);
this.principal = principal;
this.credentials = credentials;
this.setAuthenticated(false);
}
/*
常用的构造函数
authorities:用户的权限列表。
*/
public UsernamePasswordAuthenticationToken(Object principal, Object credentials, Collection<? extends GrantedAuthority> authorities) {
super(authorities);
this.principal = principal;
this.credentials = credentials;
super.setAuthenticated(true);
}
/*
....
*/
}
2. Authentication接口
Authentication 对象包含了用户的身份信息(如用户名)、凭证(如密码)以及用户的权限列表。通过SecurityContextHolder.getContext().setAuthentication(authentication)
语句设置认证信息。认证成功后,Authentication 对象会被存储在 SecurityContext 中,以便在应用程序的其他部分获取当前用户的认证信息。
public interface Authentication extends Principal, Serializable {
// 获取权限
Collection<? extends GrantedAuthority> getAuthorities();
// 获取凭证
Object getCredentials();
// 获取详情
Object getDetails();
// 获取主体信息
Object getPrincipal();
// 是否已认证
boolean isAuthenticated();
// 设置是否已认证
void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException;
}
3. SecurityContextHolder类
SecurityContextHolder 是 Spring Security 中的一个核心类,用于存储和获取当前用户的安全上下文信息。它提供了一系列静态方法,用于管理 SecurityContext 对象,该对象包含了用户的认证信息和授权信息。
public class SecurityContextHolder {
public static SecurityContext getContext() {
return strategy.getContext();
}
/*
....
*/
}
SecurityContext接口提供了获取和设置认证信息的方法。
public interface SecurityContext extends Serializable {
Authentication getAuthentication();
void setAuthentication(Authentication authentication);
}
链接:
spring security中文文档:https://springdoc.cn/spring-security/index.html
SecurityContext接口提供了获取和设置认证信息的方法。
public interface SecurityContext extends Serializable {
Authentication getAuthentication();
void setAuthentication(Authentication authentication);
}
链接:
spring security中文文档:https://springdoc.cn/spring-security/index.html
参考文章:https://blog.csdn.net/m0_71273766/article/details/132942056?spm=1001.2014.3001.5501