SpringBoot+SpringSecurity實(shí)現(xiàn)認(rèn)證的流程詳解
整合springSecurity
對(duì)應(yīng)springboot版本,直接加依賴,這樣版本不會(huì)錯(cuò)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
比如我這里是2.6.4的版本。對(duì)應(yīng)的springSecurity版本是5.6.x

沒(méi)找到springSecurity對(duì)應(yīng)springboot依賴對(duì)應(yīng)表
但springboot2.x基本對(duì)應(yīng)security的5.x版本
3.x對(duì)應(yīng)6.x版本
最基本的概念:
認(rèn)證和授權(quán)
- 認(rèn)證(Authentication):用戶輸入賬戶密碼,系統(tǒng)讓其登錄到系統(tǒng)里
- 授權(quán)(authorities):用戶的權(quán)限不同,他們能在系統(tǒng)做的事情都不同
springSecurity如何實(shí)現(xiàn)認(rèn)證
UsernamePasswordAuthenticationToken可以允許你傳入username和password參數(shù)
關(guān)鍵代碼
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(user.getUserName(),user.getPassword());
然后調(diào)用UserDetailsService的loadUserByUsername方法根據(jù)username查出數(shù)據(jù)庫(kù)中的這個(gè)用戶
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//查詢用戶信息
User user = userMapper.findByColumnAndValue("user_name", username);
if(user==null){
throw new UsernameNotFoundException("用戶名或密碼錯(cuò)誤");
}
//查詢用戶權(quán)限
List<String> perms = menuMapper.selectPermsByUserId(user.getId());
return new LoginUser(user,perms);
}
然后可以調(diào)用authenticationManager.authenticate方法對(duì)用戶輸入的賬號(hào)密碼進(jìn)行驗(yàn)證,密碼會(huì)經(jīng)過(guò)passwordEncoder去加密,然后和數(shù)據(jù)庫(kù)中該用戶的賬號(hào)密碼比對(duì)。
//加密器 bean
@Bean
public PasswordEncoder PasswordEncoder(){
return new BCryptPasswordEncoder();
}
//驗(yàn)證邏輯
Authentication authenticate = authenticationManager.authenticate(usernamePasswordAuthenticationToken);
如果通過(guò),返回一個(gè)Authentication對(duì)象,封裝了該用戶的信息。像這樣:

這時(shí)需要將信息保存到Security上下文。
像這樣:
SecurityContextHolder.getContext().setAuthentication(authenticate);
這樣,后面的代碼就可以通過(guò)SecurityContextHolder.getContext()來(lái)獲取當(dāng)前用戶了。

如果失敗,springSecurity會(huì)拋出一個(gè)異常:AuthenticationException。
框架有默認(rèn)異常處理器,但一般你可以自定義異常處理器,并把錯(cuò)誤信息和業(yè)務(wù)整合。像這樣:
@Component
public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
ResponseResult<Object> noAuthentication = ResponseResult.noAuthentication("認(rèn)證失敗");
String json = JSON.toJSONString(noAuthentication);
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Cache-Control","no-cache");
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json");
response.getWriter().println(json);
response.getWriter().flush();
}
}
其他接口如何校驗(yàn)用戶是否登錄
需要一個(gè)檢查登錄過(guò)濾器,這個(gè)過(guò)濾器要通過(guò)檢查token,并解析出用戶信息,保存到Security上下文
@Component
public class CheckLoginFilter extends OncePerRequestFilter {
@Autowired
private UserMapper userMapper;
@Autowired
private RedisCache redisCache;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
// 如果請(qǐng)求路徑是登錄接口,直接放行
String requestURI = request.getRequestURI();
if ("/user/login".equals(requestURI)) {
filterChain.doFilter(request, response);
return;
}
?
//獲取token
String token = request.getHeader("token");
if(token==null){
//springSecurity有一個(gè)過(guò)濾器會(huì)自動(dòng)檢查Context有沒(méi)有認(rèn)證
throw new RuntimeException("token為空");
}
//解析token,獲取userId
Claims claims = JwtUtils.parserClaimsFromToken(token);
if(claims==null){
throw new RuntimeException("token非法");
}
//從redis數(shù)據(jù)庫(kù)里取
Long userId = claims.get("userId", Long.class);
String redisKey="login:"+userId;
LoginUser loginUser = (LoginUser) redisCache.getCacheObject(redisKey);
if(loginUser==null){
throw new RuntimeException("沒(méi)有登錄:redis沒(méi)有登錄key");
}
//todo 從數(shù)據(jù)庫(kù)查該用戶的權(quán)限,先寫死
//將用戶信息存入Authentication
//權(quán)限存入,全局設(shè)置為該請(qǐng)求已經(jīng)認(rèn)證過(guò)
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(loginUser,null,loginUser.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
//checkLogin完成,放行
filterChain.doFilter(request,response);
?
}
}
基本流程圖

以上就是SpringBoot+SpringSecurity實(shí)現(xiàn)認(rèn)證的流程詳解的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot SpringSecurity認(rèn)證流程的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
springboot+mybatis-plus實(shí)現(xiàn)內(nèi)置的CRUD使用詳解
這篇文章主要介紹了springboot+mybatis-plus實(shí)現(xiàn)內(nèi)置的CRUD使用詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12
Java加密 消息摘要算法MAC實(shí)現(xiàn)詳解
使用MyBatis進(jìn)行簡(jiǎn)單的更新與查詢方式

