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,61 @@ 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 ().indent (4 ))
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+ .indent (2 );
129+ return HtmlTemplates .fromTemplate (OAUTH2_LOGIN_TEMPLATE )
130+ .withRawHtml ("errorMessage" , createError (isError ))
131+ .withRawHtml ("oauth2Rows" , oauth2Rows )
132+ .render ();
133+ }
134+
135+ private static String oauth2LoginLink (String contextPath , String url , String clientName ) {
136+ return HtmlTemplates .fromTemplate (OAUTH2_ROW_TEMPLATE )
137+ .withValue ("url" , contextPath + url )
138+ .withValue ("clientName" , clientName )
139+ .render ();
161140 }
162141
163142 private static String csrfToken (CsrfToken token ) {
164- return " <input type=\" hidden\" name=\" " + token .getParameterName () + "\" value=\" " + token .getToken ()
165- + "\" >\n " ;
143+ return HtmlTemplates .fromTemplate (CSRF_INPUT_TEMPLATE )
144+ .withValue ("name" , token .getParameterName ())
145+ .withValue ("value" , token .getToken ())
146+ .render ();
166147 }
167148
168149 private static String createError (boolean isError ) {
@@ -174,4 +155,53 @@ private static String createLogoutSuccess(boolean isLogoutSuccess) {
174155 : "" ;
175156 }
176157
158+ private static final String LOGIN_PAGE_TEMPLATE = """
159+ <!DOCTYPE html>
160+ <html lang="en">
161+ <head>
162+ <meta charset="utf-8">
163+ <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
164+ <meta name="description" content="">
165+ <meta name="author" content="">
166+ <title>Please sign in</title>
167+ {{cssStyle}}
168+ </head>
169+ <body>
170+ <div class="content">
171+ {{formLogin}}
172+ {{oauth2Login}}
173+ </div>
174+ </body>
175+ </html>""" ;
176+
177+ private static final String LOGIN_FORM_TEMPLATE = """
178+ <form class="login-form" method="post" action="{{loginUrl}}">
179+ <h2>Please sign in</h2>
180+ {{errorMessage}}{{logoutMessage}}
181+ <p>
182+ <label for="username" class="screenreader">Username</label>
183+ <input type="text" id="username" name="username" placeholder="Username" required autofocus>
184+ </p>
185+ <p>
186+ <label for="password" class="screenreader">Password</label>
187+ <input type="password" id="password" name="password" placeholder="Password" required>
188+ </p>
189+ {{csrf}}
190+ <button type="submit" class="primary">Sign in</button>
191+ </form>""" ;
192+
193+ private static final String CSRF_INPUT_TEMPLATE = """
194+ <input name="{{name}}" type="hidden" value="{{value}}" />
195+ """ ;
196+
197+ private static final String OAUTH2_LOGIN_TEMPLATE = """
198+ <h2>Login with OAuth 2.0</h2>
199+ {{errorMessage}}
200+ <table class="table table-striped">
201+ {{oauth2Rows}}
202+ </table>""" ;
203+
204+ private static final String OAUTH2_ROW_TEMPLATE = """
205+ <tr><td><a href="{{url}}">{{clientName}}</a></td></tr>""" ;
206+
177207}
0 commit comments