您好,登錄后才能下訂單哦!
這篇文章主要講解了“spring security中的默認(rèn)登錄頁(yè)源碼分析”,文中的講解內(nèi)容簡(jiǎn)單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來(lái)研究和學(xué)習(xí)“spring security中的默認(rèn)登錄頁(yè)源碼分析”吧!
springboot項(xiàng)目依賴(lài)
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency>
在項(xiàng)目中隨意編寫(xiě)一個(gè)接口,然后進(jìn)行訪問(wèn)
@GetMapping("/") public String hello() { return "hello, spring security"; }
在tomcat默認(rèn)端口8080,localhost:8080 下訪問(wèn)該接口,spring security會(huì)幫我們將路徑重定向到默認(rèn)的登錄頁(yè)
那么這個(gè)默認(rèn)頁(yè)是怎么來(lái)的呢?
原來(lái)Spring Security有一個(gè)默認(rèn)的WebSecurityConfigurerAdapter,發(fā)現(xiàn)其中有一個(gè)init方法,于是在這個(gè)方法打了斷點(diǎn),在應(yīng)用啟動(dòng)的時(shí)候進(jìn)行跟蹤。
跟蹤getHttp()方法,this.disableDefaults變量默認(rèn)為false,意味著將會(huì)執(zhí)行applyDefaultConfiguration(this.http);
方法。查看applyDefaultConfiguration
方法
public void init(WebSecurity web) throws Exception { // 首先配置security要攔截的哪些http請(qǐng)求 HttpSecurity http = getHttp(); web.addSecurityFilterChainBuilder(http).postBuildAction(() -> { FilterSecurityInterceptor securityInterceptor = http.getSharedObject(FilterSecurityInterceptor.class); web.securityInterceptor(securityInterceptor); }); } protected final HttpSecurity getHttp() throws Exception { if (this.http != null) { return this.http; } AuthenticationEventPublisher eventPublisher = getAuthenticationEventPublisher(); this.localConfigureAuthenticationBldr.authenticationEventPublisher(eventPublisher); AuthenticationManager authenticationManager = authenticationManager(); this.authenticationBuilder.parentAuthenticationManager(authenticationManager); Map<Class<?>, Object> sharedObjects = createSharedObjects(); this.http = new HttpSecurity(this.objectPostProcessor, this.authenticationBuilder, sharedObjects); if (!this.disableDefaults) { // 默認(rèn)的配置將會(huì)走這個(gè)分支 applyDefaultConfiguration(this.http); ClassLoader classLoader = this.context.getClassLoader(); List<AbstractHttpConfigurer> defaultHttpConfigurers = SpringFactoriesLoader .loadFactories(AbstractHttpConfigurer.class, classLoader); for (AbstractHttpConfigurer configurer : defaultHttpConfigurers) { this.http.apply(configurer); } } configure(this.http); return this.http; }
查看applyDefaultConfiguration(this.http)
方法,發(fā)現(xiàn)http對(duì)象new了一個(gè)DefaultLoginPageConfigurer
對(duì)象屬性,
private void applyDefaultConfiguration(HttpSecurity http) throws Exception { http.csrf(); http.addFilter(new WebAsyncManagerIntegrationFilter()); http.exceptionHandling(); http.headers(); http.sessionManagement(); http.securityContext(); http.requestCache(); http.anonymous(); http.servletApi(); http.apply(new DefaultLoginPageConfigurer<>()); http.logout(); }
查看DefaultLoginPageConfigurer
類(lèi)定義,發(fā)現(xiàn)它在初始化的同時(shí),它也初始化了自己的2個(gè)私有成員變量,分別是DefaultLoginPageGeneratingFilter
默認(rèn)登錄頁(yè)面生成Filter,DefaultLogoutPageGeneratingFilter
默認(rèn)登錄頁(yè)面Filter, 名字起得很好,見(jiàn)名知意,我們馬山知道這2個(gè)類(lèi)的含義。
查看DefaultLoginPageGeneratingFilter
的類(lèi)成員變量,發(fā)現(xiàn)定義了一系列跟登錄有關(guān)的成員變量,包括登錄、登錄等路徑,默認(rèn)的登錄頁(yè)面路徑是"/login"
public class DefaultLoginPageGeneratingFilter extends GenericFilterBean { public static final String DEFAULT_LOGIN_PAGE_URL = "/login"; public static final String ERROR_PARAMETER_NAME = "error"; private String loginPageUrl; private String logoutSuccessUrl; private String failureUrl; private boolean formLoginEnabled; .....
再結(jié)合類(lèi)名思考,發(fā)現(xiàn)是個(gè)Filter類(lèi),那么它們應(yīng)該都會(huì)重新Filter的doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
方法,我們查看一下DefaultLoginPageConfigurer
類(lèi)的``doFilter方法,果然,在
doFilter`方法中發(fā)現(xiàn)了生成默認(rèn)登錄頁(yè)面的方法。
private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { // 判斷當(dāng)前的請(qǐng)求是否被認(rèn)證通過(guò) boolean loginError = isErrorPage(request); boolean logoutSuccess = isLogoutSuccess(request); if (isLoginUrlRequest(request) || loginError || logoutSuccess) { // 當(dāng)前請(qǐng)求認(rèn)證失敗的話,將會(huì)執(zhí)行這個(gè)分支 String loginPageHtml = generateLoginPageHtml(request, loginError, logoutSuccess); response.setContentType("text/html;charset=UTF-8"); response.setContentLength(loginPageHtml.getBytes(StandardCharsets.UTF_8).length); response.getWriter().write(loginPageHtml); return; } chain.doFilter(request, response); } private String generateLoginPageHtml(HttpServletRequest request, boolean loginError, boolean logoutSuccess) { String errorMsg = "Invalid credentials"; if (loginError) { HttpSession session = request.getSession(false); if (session != null) { AuthenticationException ex = (AuthenticationException) session .getAttribute(WebAttributes.AUTHENTICATION_EXCEPTION); errorMsg = (ex != null) ? ex.getMessage() : "Invalid credentials"; } } String contextPath = request.getContextPath(); StringBuilder sb = new StringBuilder(); sb.append("<!DOCTYPE html>\n"); sb.append("<html lang=\"en\">\n"); sb.append(" <head>\n"); sb.append(" <meta charset=\"utf-8\">\n"); sb.append(" <meta name=\"viewport\" content=\"width=device-width, initial-scale=1, shrink-to-fit=no\">\n"); sb.append(" <meta name=\"description\" content=\"\">\n"); sb.append(" <meta name=\"author\" content=\"\">\n"); sb.append(" <title>Please sign in</title>\n"); sb.append(" <link href=\"https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css\" " + "rel=\"stylesheet\" integrity=\"sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M\" crossorigin=\"anonymous\">\n"); sb.append(" <link href=\"https://getbootstrap.com/docs/4.0/examples/signin/signin.css\" " + "rel=\"stylesheet\" crossorigin=\"anonymous\"/>\n"); sb.append(" </head>\n"); sb.append(" <body>\n"); sb.append(" <div class=\"container\">\n"); if (this.formLoginEnabled) { sb.append(" <form class=\"form-signin\" method=\"post\" action=\"" + contextPath + this.authenticationUrl + "\">\n"); sb.append(" <h3 class=\"form-signin-heading\">Please sign in</h3>\n"); sb.append(createError(loginError, errorMsg) + createLogoutSuccess(logoutSuccess) + " <p>\n"); sb.append(" <label for=\"username\" class=\"sr-only\">Username</label>\n"); sb.append(" <input type=\"text\" id=\"username\" name=\"" + this.usernameParameter + "\" class=\"form-control\" placeholder=\"Username\" required autofocus>\n"); sb.append(" </p>\n"); sb.append(" <p>\n"); sb.append(" <label for=\"password\" class=\"sr-only\">Password</label>\n"); sb.append(" <input type=\"password\" id=\"password\" name=\"" + this.passwordParameter + "\" class=\"form-control\" placeholder=\"Password\" required>\n"); sb.append(" </p>\n"); sb.append(createRememberMe(this.rememberMeParameter) + renderHiddenInputs(request)); sb.append(" <button class=\"btn btn-lg btn-primary btn-block\" type=\"submit\">Sign in</button>\n"); sb.append(" </form>\n"); } if (this.openIdEnabled) { sb.append(" <form name=\"oidf\" class=\"form-signin\" method=\"post\" action=\"" + contextPath + this.openIDauthenticationUrl + "\">\n"); sb.append(" <h3 class=\"form-signin-heading\">Login with OpenID Identity</h3>\n"); ...... return sb.toString(); }
我們發(fā)現(xiàn)generateLoginPageHtml(HttpServletRequest request, boolean loginError, boolean logoutSuccess)
這個(gè)方法中使用了最原始的Servlet寫(xiě)html頁(yè)面的方法,將登錄頁(yè)的html代碼寫(xiě)到字符串中寫(xiě)出到前端展示。
感謝各位的閱讀,以上就是“spring security中的默認(rèn)登錄頁(yè)源碼分析”的內(nèi)容了,經(jīng)過(guò)本文的學(xué)習(xí)后,相信大家對(duì)spring security中的默認(rèn)登錄頁(yè)源碼分析這一問(wèn)題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是億速云,小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。