1919import java .nio .charset .Charset ;
2020import java .util .HashMap ;
2121import java .util .Map ;
22+ import java .util .stream .Collectors ;
2223
2324import reactor .core .publisher .Mono ;
2425
3738import org .springframework .web .server .ServerWebExchange ;
3839import org .springframework .web .server .WebFilter ;
3940import org .springframework .web .server .WebFilterChain ;
40- import org .springframework .web .util .HtmlUtils ;
4141
4242/**
4343 * Generates a default log in page used for authenticating users.
@@ -89,80 +89,60 @@ private Mono<DataBuffer> createBuffer(ServerWebExchange exchange) {
8989 private byte [] createPage (ServerWebExchange exchange , String csrfTokenHtmlInput ) {
9090 MultiValueMap <String , String > queryParams = exchange .getRequest ().getQueryParams ();
9191 String contextPath = exchange .getRequest ().getPath ().contextPath ().value ();
92- StringBuilder page = new StringBuilder ();
93- page .append ("<!DOCTYPE html>\n " );
94- page .append ("<html lang=\" en\" >\n " );
95- page .append (" <head>\n " );
96- page .append (" <meta charset=\" utf-8\" >\n " );
97- page .append (" <meta name=\" viewport\" content=\" width=device-width, initial-scale=1, shrink-to-fit=no\" >\n " );
98- page .append (" <meta name=\" description\" content=\" \" >\n " );
99- page .append (" <meta name=\" author\" content=\" \" >\n " );
100- page .append (" <title>Please sign in</title>\n " );
101- page .append (CssUtils .getCssStyleBlock ().indent (4 ));
102- page .append (" </head>\n " );
103- page .append (" <body>\n " );
104- page .append (" <div class=\" content\" >\n " );
105- page .append (formLogin (queryParams , contextPath , csrfTokenHtmlInput ));
106- page .append (oauth2LoginLinks (queryParams , contextPath , this .oauth2AuthenticationUrlToClientName ));
107- page .append (" </div>\n " );
108- page .append (" </body>\n " );
109- page .append ("</html>" );
110- return page .toString ().getBytes (Charset .defaultCharset ());
92+
93+ return HtmlTemplates .fromTemplate (LOGIN_PAGE_TEMPLATE )
94+ .withRawHtml ("cssStyle" , CssUtils .getCssStyleBlock ())
95+ .withRawHtml ("formLogin" , formLogin (queryParams , contextPath , csrfTokenHtmlInput ))
96+ .withRawHtml ("oauth2Login" , oauth2Login (queryParams , contextPath , this .oauth2AuthenticationUrlToClientName ))
97+ .render ()
98+ .getBytes (Charset .defaultCharset ());
11199 }
112100
113101 private String formLogin (MultiValueMap <String , String > queryParams , String contextPath , String csrfTokenHtmlInput ) {
114102 if (!this .formLoginEnabled ) {
115103 return "" ;
116104 }
105+
117106 boolean isError = queryParams .containsKey ("error" );
118107 boolean isLogoutSuccess = queryParams .containsKey ("logout" );
119- StringBuilder page = new StringBuilder ();
120- page .append (" <form class=\" login-form\" method=\" post\" action=\" " + contextPath + "/login\" >\n " );
121- page .append (" <h2>Please sign in</h2>\n " );
122- page .append (createError (isError ));
123- page .append (createLogoutSuccess (isLogoutSuccess ));
124- page .append (" <p>\n " );
125- page .append (" <label for=\" username\" class=\" screenreader\" >Username</label>\n " );
126- page .append (" <input type=\" text\" id=\" username\" name=\" username\" "
127- + "placeholder=\" Username\" required autofocus>\n " );
128- page .append (" </p>\n " + " <p>\n " );
129- page .append (" <label for=\" password\" class=\" screenreader\" >Password</label>\n " );
130- page .append (" <input type=\" password\" id=\" password\" name=\" password\" "
131- + "placeholder=\" Password\" required>\n " );
132- page .append (" </p>\n " );
133- page .append (csrfTokenHtmlInput );
134- page .append (" <button class=\" primary\" type=\" submit\" >Sign in</button>\n " );
135- page .append (" </form>\n " );
136- return page .toString ();
108+
109+ return HtmlTemplates .fromTemplate (LOGIN_FORM_TEMPLATE )
110+ .withValue ("loginUrl" , contextPath + "/login" )
111+ .withRawHtml ("errorMessage" , createError (isError ))
112+ .withRawHtml ("logoutMessage" , createLogoutSuccess (isLogoutSuccess ))
113+ .withRawHtml ("csrf" , csrfTokenHtmlInput )
114+ .render ();
137115 }
138116
139- private static String oauth2LoginLinks (MultiValueMap <String , String > queryParams , String contextPath ,
117+ private static String oauth2Login (MultiValueMap <String , String > queryParams , String contextPath ,
140118 Map <String , String > oauth2AuthenticationUrlToClientName ) {
141119 if (oauth2AuthenticationUrlToClientName .isEmpty ()) {
142120 return "" ;
143121 }
144122 boolean isError = queryParams .containsKey ("error" );
145- StringBuilder sb = new StringBuilder ();
146- sb . append ( "<div class= \" content \" ><h2>Login with OAuth 2.0</h2>" );
147- sb . append ( createError ( isError ));
148- sb . append ( "<table class= \" table table-striped \" > \n " );
149- for ( Map . Entry < String , String > clientAuthenticationUrlToClientName : oauth2AuthenticationUrlToClientName
150- . entrySet ()) {
151- sb . append ( " <tr><td>" );
152- String url = clientAuthenticationUrlToClientName . getKey ();
153- sb . append ( "<a href= \" " ). append ( contextPath ). append ( url ). append ( " \" >" );
154- String clientName = HtmlUtils . htmlEscape ( clientAuthenticationUrlToClientName . getValue ());
155- sb . append ( clientName );
156- sb . append ( "</a>" );
157- sb . append ( "</td></tr> \n " );
158- }
159- sb . append ( "</table></div> \n " );
160- return sb . toString ();
123+
124+ String oauth2Rows = oauth2AuthenticationUrlToClientName . entrySet ()
125+ . stream ()
126+ . map (( urlToName ) -> oauth2LoginLink ( contextPath , urlToName . getKey (), urlToName . getValue ()))
127+ . collect ( Collectors . joining ( " \n " ));
128+ return HtmlTemplates . fromTemplate ( OAUTH2_LOGIN_TEMPLATE )
129+ . withRawHtml ( "errorMessage" , createError ( isError ))
130+ . withRawHtml ( "oauth2Rows" , oauth2Rows )
131+ . render ( );
132+ }
133+
134+ private static String oauth2LoginLink ( String contextPath , String url , String clientName ) {
135+ return HtmlTemplates . fromTemplate ( OAUTH2_ROW_TEMPLATE )
136+ . withValue ( "url" , contextPath + url )
137+ . withValue ( "clientName" , clientName )
138+ . render ();
161139 }
162140
163141 private static String csrfToken (CsrfToken token ) {
164- return " <input type=\" hidden\" name=\" " + token .getParameterName () + "\" value=\" " + token .getToken ()
165- + "\" >\n " ;
142+ return HtmlTemplates .fromTemplate (CSRF_INPUT_TEMPLATE )
143+ .withValue ("name" , token .getParameterName ())
144+ .withValue ("value" , token .getToken ())
145+ .render ();
166146 }
167147
168148 private static String createError (boolean isError ) {
@@ -174,4 +154,53 @@ private static String createLogoutSuccess(boolean isLogoutSuccess) {
174154 : "" ;
175155 }
176156
157+ private static final String LOGIN_PAGE_TEMPLATE = """
158+ <!DOCTYPE html>
159+ <html lang="en">
160+ <head>
161+ <meta charset="utf-8">
162+ <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
163+ <meta name="description" content="">
164+ <meta name="author" content="">
165+ <title>Please sign in</title>
166+ {{cssStyle}}
167+ </head>
168+ <body>
169+ <div class="content">
170+ {{formLogin}}
171+ {{oauth2Login}}
172+ </div>
173+ </body>
174+ </html>""" ;
175+
176+ private static final String LOGIN_FORM_TEMPLATE = """
177+ <form class="login-form" method="post" action="{{loginUrl}}">
178+ <h2>Please sign in</h2>
179+ {{errorMessage}}{{logoutMessage}}
180+ <p>
181+ <label for="username" class="screenreader">Username</label>
182+ <input type="text" id="username" name="username" placeholder="Username" required autofocus>
183+ </p>
184+ <p>
185+ <label for="password" class="screenreader">Password</label>
186+ <input type="password" id="password" name="password" placeholder="Password" required>
187+ </p>
188+ {{csrf}}
189+ <button type="submit" class="primary">Sign in</button>
190+ </form>""" ;
191+
192+ private static final String CSRF_INPUT_TEMPLATE = """
193+ <input name="{{name}}" type="hidden" value="{{value}}" />
194+ """ ;
195+
196+ private static final String OAUTH2_LOGIN_TEMPLATE = """
197+ <h2>Login with OAuth 2.0</h2>
198+ {{errorMessage}}
199+ <table class="table table-striped">
200+ {{oauth2Rows}}
201+ </table>""" ;
202+
203+ private static final String OAUTH2_ROW_TEMPLATE = """
204+ \t <tr><td><a href="{{url}}">{{clientName}}</a></td></tr>""" ;
205+
177206}
0 commit comments