SpringSecurity認(rèn)證授權(quán)全流程解讀
SpringSecurity認(rèn)證流程:loadUserByUsername()方法內(nèi)部實(shí)現(xiàn)。
實(shí)現(xiàn)步驟
構(gòu)建一個(gè)自定義的service接口,實(shí)現(xiàn)SpringSecurity的UserDetailService接口。
建一個(gè)service實(shí)現(xiàn)類,實(shí)現(xiàn)此loadUserByUsername方法。
調(diào)用登錄的login接口,會(huì)經(jīng)過authenticationManager.authenticate(authenticationToken)方法。此方法會(huì)調(diào)用loadUserByUsername方法。
方法內(nèi)部做用戶信息的查詢,判斷用戶名和密碼是否正確,這是第一道認(rèn)證。
@Service
@RequiredArgsConstructor
public class UserDetailsServiceImpl implements UserDetailsService {
private final SysUserMapper sysUserMapper;
//用戶登錄請(qǐng)求/login,自動(dòng)調(diào)用方法
//根據(jù)用戶名獲取用戶信息
//UserDetails 存儲(chǔ)用戶信息,包括用戶名,密碼,權(quán)限
@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("用戶名不存在");
}
//認(rèn)證成功回UserDetails對(duì)象
return new LoginUser(sysUser);
}
}
@ToString
public class LoginUser implements UserDetails {
private SysUser sysUser;
public LoginUser(SysUser sysUser) {
this.sysUser = sysUser;
}
// 權(quán)限
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return List.of();
}
@Override
public String getPassword() {
return sysUser.getPassword();
}
@Override
public String getUsername() {
return sysUser.getUsername();
}
// 賬號(hào)是否過期
@Override
public boolean isAccountNonExpired() {
return true;
}
// 賬號(hào)是否被鎖定
@Override
public boolean isAccountNonLocked() {
return true;
}
// 密碼是否過期
@Override
public boolean isCredentialsNonExpired() {
return true;
}
// 賬號(hào)是否可用
@Override
public boolean isEnabled() {
return true;
}
}如果沒有查到信息就拋出異常。
如果查到信息了再接著查用戶的權(quán)限信息,返回權(quán)限信息到loginUser實(shí)體。
此實(shí)體實(shí)現(xiàn)了SpringSecurity自帶的userDetail接口。實(shí)現(xiàn)了getAuthorities方法。
每次查詢權(quán)限都會(huì)調(diào)用此方法。
查詢到的權(quán)限,會(huì)被返回到login接口。進(jìn)行后續(xù)操作。
如果認(rèn)證通過,通過身份信息中的userid生產(chǎn)一個(gè)jwt。
把完整的用戶信息作為value,token作為key存入redis。
@RestController
@Tag(name = "認(rèn)證模塊", description = "認(rèn)證模塊")
@RequestMapping("/auth")
@RequiredArgsConstructor
public class AuthController {
//注入AuthenticationManager(認(rèn)證管理器)
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);
//登錄邏輯
//調(diào)用UserDetailsService.loadUserByUsername方法獲取
//不能直接調(diào)用,需要通過AuthenticationManager進(jìn)行認(rèn)證
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);
}
//認(rèn)證成功方法token
String token=jwtUtils.generateToken(authenticate);
return Result.success(token);
}
}
@Configuration
@EnableWebSecurity // 開啟web安全
@EnableMethodSecurity
@RequiredArgsConstructor
public class SecurityConfig {
private final IgnoredUrl ignoredUrl;
/**
* 配置認(rèn)證管理器 AuthenticationManager
* 作用:用于身份認(rèn)證
* 參數(shù):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 {
//關(guān)閉csrf防護(hù),否則回導(dǎo)致登錄失敗
http.csrf(a -> a.disable()); //禁用SCRF
//配置安全攔截規(guī)則
http.authorizeHttpRequests(req ->
req.requestMatchers(ignoredUrl.getUrls())
.permitAll()
.anyRequest().authenticated());
/**
* 配置登錄頁
*/
http.formLogin(form -> form
.loginPage("/")
.successForwardUrl("/index") //登錄成功跳轉(zhuǎn)頁面
.loginProcessingUrl("/login")//登錄處理url
.failureForwardUrl("/error") //登錄失敗跳轉(zhuǎn)頁面
// .usernameParameter("name")//自定義用戶名參數(shù)
// .passwordParameter("password")//自定義密碼參數(shù)
);
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;
}
登錄成功

登陸失敗

總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
SpringSession 請(qǐng)求與響應(yīng)重寫的實(shí)現(xiàn)
這篇文章主要介紹了SpringSession 請(qǐng)求與響應(yīng)重寫的實(shí)現(xiàn),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-11-11
CountDownLatch源碼解析之a(chǎn)wait()
這篇文章主要為大家詳細(xì)解析了CountDownLatch源碼之a(chǎn)wait方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-04-04
Spring與Mybatis相結(jié)合實(shí)現(xiàn)多數(shù)據(jù)源切換功能
這篇文章主要介紹了Spring與Mybatis相結(jié)合實(shí)現(xiàn)多數(shù)據(jù)源切換功能的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2016-06-06
使用springboot結(jié)合vue實(shí)現(xiàn)sso單點(diǎn)登錄
這篇文章主要為大家詳細(xì)介紹了如何使用springboot+vue實(shí)現(xiàn)sso單點(diǎn)登錄,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-06-06
詳細(xì)分析Java并發(fā)集合LinkedBlockingQueue的用法
這篇文章主要介紹了詳細(xì)分析Java并發(fā)集合LinkedBlockingQueue的用法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-04-04
springboot如何接收get和post請(qǐng)求參數(shù)
這篇文章主要介紹了springboot如何接收get和post請(qǐng)求參數(shù),本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-06-06

