主要是通過Authentication封裝
接口定義
public interface Authentication extends Principal, Serializable { //用來獲取用戶的權限。 Collection<? extends GrantedAuthority> getAuthorities(); //方法用來獲取用戶憑證,一般來說就是密碼。 Object getCredentials(); //方法用來獲取用戶攜帶的詳細信息,可能是當前請求之類的東西。 Object getDetails(); //方法用來獲取當前用戶,可能是一個用戶名,也可能是一個用戶對象。 Object getPrincipal(); //當前用戶是否認證成功。 boolean isAuthenticated(); void setAuthenticated(boolean var1) throws IllegalArgumentException; }
如何獲取
@GetMapping("/hello") public String hello() { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); return "hello"; }
getDetails方法
可以看到打印了用戶id和sessionId信息
@GetMapping("/hello") public String hello() { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); WebAuthenticationDetails details = (WebAuthenticationDetails) authentication.getDetails(); System.out.println(details); return "hello"; }
打印
org.springframework.security.web.authentication.WebAuthenticationDetails@0: RemoteIpAddress: 127.0.0.1; SessionId: C1886FD11CACBAD7387B9273442BC212
源碼
1.org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter#attemptAuthentication
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { if (this.postOnly && !request.getMethod().equals("POST")) { throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod()); } else { String username = this.obtainUsername(request); String password = this.obtainPassword(request); if (username == null) { username = ""; } if (password == null) { password = ""; } username = username.trim(); UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password); //為authRequest 的detail賦值 UsernamePasswordAuthenticationToken實現了Authentication this.setDetails(request, authRequest); return this.getAuthenticationManager().authenticate(authRequest); } }
2.org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter#setDetails
protected AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource = new WebAuthenticationDetailsSource(); protected void setDetails(HttpServletRequest request, UsernamePasswordAuthenticationToken authRequest) { //UsernamePasswordAuthenticationToken是通過authenticationDetailsSource 構建的detail數據 authRequest.setDetails(this.authenticationDetailsSource.buildDetails(request)); }
3.WebAuthenticationDetailsSource的定義
public class WebAuthenticationDetailsSource implements AuthenticationDetailsSource<HttpServletRequest, WebAuthenticationDetails> { public WebAuthenticationDetailsSource() { } public WebAuthenticationDetails buildDetails(HttpServletRequest context) { //我們通過Authentication.getDetails()獲取的就是WebAuthenticationDetails return new WebAuthenticationDetails(context); } }
4.WebAuthenticationDetails定義
public class WebAuthenticationDetails implements Serializable { private static final long serialVersionUID = 520L; private final String remoteAddress; private final String sessionId; public WebAuthenticationDetails(HttpServletRequest request) { //從request獲取ip this.remoteAddress = request.getRemoteAddr(); //獲取sessionId HttpSession session = request.getSession(false); this.sessionId = session != null ? session.getId() : null; } }
自定義
看上面源碼我們知道入口是在UsernamePasswordAuthenticationFilter進行Authentication的detail設置的 如何構建detail對象 是委托給AuthenticationDetailsSource默認是WebAuthenticationDetailsSource
所以我們自定義就要自定義WebAuthenticationDetailsSource和封裝數據的Dtail對象
通過自定優化《spring-security使用-更友好的方式擴展登錄AuthenticationProvider(三)》 驗證碼校驗邏輯
1.自定義的Detail對象
*/ public class MyWebAuthenticationDetails extends WebAuthenticationDetails { //封裝提交的驗證碼信息 private String code; private HttpServletRequest httpServletRequest; public MyWebAuthenticationDetails(HttpServletRequest request) { super(request); //獲得驗證碼 this.code=request.getParameter("code"); this.httpServletRequest=request; } /** * 增加一個檢查驗證碼的方法 * @return */ public boolean checkCode(){ String verify_code = (String) httpServletRequest.getSession().getAttribute("verify_code"); if (code == null || verify_code == null || !code.equals(verify_code)) { throw new AuthenticationServiceException("驗證碼錯誤"); } return true; } }
2.自定義構建對象
public class MyWebAuthenticationDetailsSource implements AuthenticationDetailsSource<HttpServletRequest, MyWebAuthenticationDetails> { @Override public MyWebAuthenticationDetails buildDetails(HttpServletRequest request) { return new MyWebAuthenticationDetails(request); } }
3.使用自定義的構建對象替換默認的
protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .anyRequest().authenticated() .and() .rememberMe() .key("system") .and() .formLogin() .authenticationDetailsSource(new MyWebAuthenticationDetailsSource()) .usernameParameter("loginName") .passwordParameter("loginPassword") .defaultSuccessUrl("/hello") .failureForwardUrl("/loginFail") .failureUrl("/login.html") .permitAll()//不攔截 .and() .csrf()//記得關閉 .disable(); }