SpringSecurity认证授权完整流程
SpringSecurity认证流程:loadUserByUsername()方法内部实现。
实现步骤:
- 构建一个自定义的service接口,实现SpringSecurity的UserDetailService接口。
- 建一个service实现类,实现此loadUserByUsername方法。
- 调用登录的login接口,会经过authenticationManager.authenticate(authenticationToken)方法。此方法会调用loadUserByUsername方法。
- 方法内部做用户信息的查询,判断用户名和密码是否正确,这是第一道认证。
@Service
@RequiredArgsConstructor
public class UserDetailsServiceImpl implements UserDetailsService {
private final SysUserMapper sysUserMapper;
//用户登录请求/login,自动调用方法
//根据用户名获取用户信息
//UserDetails 存储用户信息,包括用户名,密码,权限
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
LambdaQueryWrapper<SysUser> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(SysUser::getUsername, username);
SysUser sysUser = sysUserMapper.selectOne(wrapper);
if (Objects.isNull(sysUser)){
throw new UsernameNotFoundException("用户名不存在");
}
//认证成功回UserDetails对象
return new LoginUser(sysUser);
}
}
@ToString
public class LoginUser implements UserDetails {
private SysUser sysUser;
public LoginUser(SysUser sysUser) {
this.sysUser = sysUser;
}
// 权限
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return List.of();
}
@Override
public String getPassword() {
return sysUser.getPassword();
}
@Override
public String getUsername() {
return sysUser.getUsername();
}
// 账号是否过期
@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.如果没有查到信息就抛出异常。
6.如果查到信息了再接着查用户的权限信息,返回权限信息到loginUser实体。
7.此实体实现了SpringSecurity自带的userDetail接口。实现了getAuthorities方法。
.8每次查询权限都会调用此方法。
9.查询到的权限,会被返回到login接口。进行后续操作。
10.如果认证通过,通过身份信息中的userid生产一个jwt。
11.把完整的用户信息作为value,token作为key存入redis。
@RestController
@Tag(name = "认证模块", description = "认证模块")
@RequestMapping("/auth")
@RequiredArgsConstructor
public class AuthController {
//注入AuthenticationManager(认证管理器)
private final AuthenticationManager authenticationManager;
private final JwtUtils jwtUtils;
@PostMapping("/login")
@Operation(summary = "登录")
public Result login(@RequestParam("username") String username, @RequestParam("password") String password) {
System.out.println(username + password);
//登录逻辑
//调用UserDetailsService.loadUserByUsername方法获取
//不能直接调用,需要通过AuthenticationManager进行认证
Authentication authentication = new UsernamePasswordAuthenticationToken(username, password);
Authentication authenticate = null;
try {
authenticate = authenticationManager.authenticate(authentication);
} catch (BadCredentialsException e) {
return Result.failed(ResultCode.USERNAME_OR_PASSWORD_ERROR);
}
//认证成功方法token
String token=jwtUtils.generateToken(authenticate);
return Result.success(token);
}
}
@Configuration
@EnableWebSecurity // 开启web安全
@EnableMethodSecurity
@RequiredArgsConstructor
public class SecurityConfig {
private final IgnoredUrl ignoredUrl;
/**
* 配置认证管理器 AuthenticationManager
* 作用:用于身份认证
* 参数:UserDetailsService, PasswordEncoder
*/
@Bean
public AuthenticationManager authenticationManager(UserDetailsService userDetailsService, PasswordEncoder passwordEncoder) {
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setUserDetailsService(userDetailsService);
provider.setPasswordEncoder(passwordEncoder);
return new ProviderManager(provider);
}
/**
* 密码编码器
*
* @return
*/
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
//关闭csrf防护,否则回导致登录失败
http.csrf(a -> a.disable()); //禁用SCRF
//配置安全拦截规则
http.authorizeHttpRequests(req ->
req.requestMatchers(ignoredUrl.getUrls())
.permitAll()
.anyRequest().authenticated());
/**
* 配置登录页
*/
http.formLogin(form -> form
.loginPage("/")
.successForwardUrl("/index") //登录成功跳转页面
.loginProcessingUrl("/login")//登录处理url
.failureForwardUrl("/error") //登录失败跳转页面
// .usernameParameter("name")//自定义用户名参数
// .passwordParameter("password")//自定义密码参数
);
return http.build();
}
}
@Component
public class JwtUtils {
@Value("${jwt.secret}")
private String secret;
@Value("${jwt.expiration}")
private Integer expiration;
/**
*
*/
public String generateToken(Authentication authentication) {
Date now = new Date();
Date expirationDate = DateUtil.offsetSecond(now, expiration);
Map<String, Object> claims = new HashMap<>();
claims.put("username", authentication.getName());//用户名
claims.put("exp", expirationDate);
// claims.put();
return JWTUtil.createToken(claims, secret.getBytes());
}
}
@Component
@Data
@ConfigurationProperties(prefix = "security.ignored")
@ToString
public class IgnoredUrl {
private String[] urls;
}
登录成功
登陆失败