问:
Unable to Authenticate Using Form Authentication
I'm having trouble figuring out why I am unable to authenticate. After looking at the trace I think the session isn't being created, or is cleared, or something of that nature. There is a block of code in my Admin Controller login post method that if uncommented the authentication works, however I don't want to manually do this, as Spring should handle it for me. Any help would be appreciated, and if I can include additional source code to help, let me know.

Details:

I am using Spring Boot, Spring Security, Thymleaf and running this through Localhost.

Every time I view a page on /admin/** the login page appears and I need to provide credentials. When the credentials are entered, the login page processes and displays /admin/index (as per the controller), however when I try navigating to another page (say: /admin/orgs) the login screen reappears. Aside from the obvious, I know that the user is not being authenticated as I check if the user is authenticated on /admin/index.

Project Structure

```<code>+ src
    + main
        + java
            - org.neric (package)
                - SecurityConfig
                - AdminController
                - AppController
                - Application
                - WebConfig
        + resources
            + templates
                - index.html
                + admin
                    - index.html
                    - login.html
                    - orgs.html
            + static
                + css
                + js
                + img
</code>```

Trace (application.properties trace=true)

```<code>2020-05-29 10:47:32.511 DEBUG 15316 --- [nio-8080-exec-1] o.a.tomcat.util.net.SocketWrapperBase    : Socket: [org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper@77ed1e40:org.apache.tomcat.util.net.NioChannel@78cb69e6:java.nio.channels.SocketChannel[connected local=/0:0:0:0:0:0:0:1:8080 remote=/0:0:0:0:0:0:0:1:50574]], Read from buffer: [0]
2020-05-29 10:47:32.511 DEBUG 15316 --- [nio-8080-exec-1] org.apache.tomcat.util.net.NioEndpoint   : Socket: [org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper@77ed1e40:org.apache.tomcat.util.net.NioChannel@78cb69e6:java.nio.channels.SocketChannel[connected local=/0:0:0:0:0:0:0:1:8080 remote=/0:0:0:0:0:0:0:1:50574]], Read direct from socket: [872]
2020-05-29 10:47:32.512 DEBUG 15316 --- [nio-8080-exec-1] o.a.t.util.http.Rfc6265CookieProcessor   : Cookies: Parsing b[]: sidenav-state=pinned; JSESSIONID=408BA31320628AA0E4E3E3D9FBEDC8DD
2020-05-29 10:47:32.512 DEBUG 15316 --- [nio-8080-exec-1] o.a.catalina.connector.CoyoteAdapter     :  Requested cookie session id is 408BA31320628AA0E4E3E3D9FBEDC8DD
2020-05-29 10:47:32.512 DEBUG 15316 --- [nio-8080-exec-1] o.a.c.authenticator.AuthenticatorBase    : Security checking request POST /admin/login
2020-05-29 10:47:32.512 DEBUG 15316 --- [nio-8080-exec-1] org.apache.catalina.realm.RealmBase      :   No applicable constraints defined
2020-05-29 10:47:32.512 DEBUG 15316 --- [nio-8080-exec-1] o.a.c.authenticator.AuthenticatorBase    :  Not subject to any constraint
2020-05-29 10:47:32.512 DEBUG 15316 --- [nio-8080-exec-1] o.apache.catalina.core.StandardWrapper   :   Returning non-STM instance
2020-05-29 10:47:32.513 DEBUG 15316 --- [nio-8080-exec-1] org.apache.tomcat.util.http.Parameters   : Set encoding to UTF-8
2020-05-29 10:47:32.513 DEBUG 15316 --- [nio-8080-exec-1] org.apache.tomcat.util.http.Parameters   : Start processing with input [%24%7B_csrf.parameterName%7D=%24%7B_csrf.token%7D&email=admin%40neric.org&password=123456]
2020-05-29 10:47:32.513 TRACE 15316 --- [nio-8080-exec-1] o.s.b.w.s.f.OrderedRequestContextFilter  : Bound request context to thread: org.apache.catalina.connector.RequestFacade@2b59dd1c
2020-05-29 10:47:32.513 DEBUG 15316 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy        : /admin/login at position 1 of 11 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
2020-05-29 10:47:32.513 DEBUG 15316 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy        : /admin/login at position 2 of 11 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
2020-05-29 10:47:32.513 DEBUG 15316 --- [nio-8080-exec-1] w.c.HttpSessionSecurityContextRepository : No HttpSession currently exists
2020-05-29 10:47:32.513 DEBUG 15316 --- [nio-8080-exec-1] w.c.HttpSessionSecurityContextRepository : No SecurityContext was available from the HttpSession: null. A new one will be created.
2020-05-29 10:47:32.513 DEBUG 15316 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy        : /admin/login at position 3 of 11 in additional filter chain; firing Filter: 'HeaderWriterFilter'
2020-05-29 10:47:32.513 DEBUG 15316 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy        : /admin/login at position 4 of 11 in additional filter chain; firing Filter: 'LogoutFilter'
2020-05-29 10:47:32.513 DEBUG 15316 --- [nio-8080-exec-1] o.s.s.web.util.matcher.OrRequestMatcher  : Trying to match using Ant [pattern='/admin/logout', GET]
2020-05-29 10:47:32.513 DEBUG 15316 --- [nio-8080-exec-1] o.s.s.w.u.matcher.AntPathRequestMatcher  : Request 'POST /admin/login' doesn't match 'GET /admin/logout'
2020-05-29 10:47:32.513 DEBUG 15316 --- [nio-8080-exec-1] o.s.s.web.util.matcher.OrRequestMatcher  : Trying to match using Ant [pattern='/admin/logout', POST]
2020-05-29 10:47:32.513 DEBUG 15316 --- [nio-8080-exec-1] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/admin/login'; against '/admin/logout'
2020-05-29 10:47:32.513 DEBUG 15316 --- [nio-8080-exec-1] o.s.s.web.util.matcher.OrRequestMatcher  : Trying to match using Ant [pattern='/admin/logout', PUT]
2020-05-29 10:47:32.513 DEBUG 15316 --- [nio-8080-exec-1] o.s.s.w.u.matcher.AntPathRequestMatcher  : Request 'POST /admin/login' doesn't match 'PUT /admin/logout'
2020-05-29 10:47:32.513 DEBUG 15316 --- [nio-8080-exec-1] o.s.s.web.util.matcher.OrRequestMatcher  : Trying to match using Ant [pattern='/admin/logout', DELETE]
2020-05-29 10:47:32.513 DEBUG 15316 --- [nio-8080-exec-1] o.s.s.w.u.matcher.AntPathRequestMatcher  : Request 'POST /admin/login' doesn't match 'DELETE /admin/logout'
2020-05-29 10:47:32.514 DEBUG 15316 --- [nio-8080-exec-1] o.s.s.web.util.matcher.OrRequestMatcher  : No matches found
2020-05-29 10:47:32.514 DEBUG 15316 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy        : /admin/login at position 5 of 11 in additional filter chain; firing Filter: 'UsernamePasswordAuthenticationFilter'
2020-05-29 10:47:32.514 DEBUG 15316 --- [nio-8080-exec-1] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/admin/login'; against 'admin/login'
2020-05-29 10:47:32.514 DEBUG 15316 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy        : /admin/login at position 6 of 11 in additional filter chain; firing Filter: 'RequestCacheAwareFilter'
2020-05-29 10:47:32.514 DEBUG 15316 --- [nio-8080-exec-1] o.s.s.w.s.HttpSessionRequestCache        : saved request doesn't match
2020-05-29 10:47:32.514 DEBUG 15316 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy        : /admin/login at position 7 of 11 in additional filter chain; firing Filter: 'SecurityContextHolderAwareRequestFilter'
2020-05-29 10:47:32.514 DEBUG 15316 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy        : /admin/login at position 8 of 11 in additional filter chain; firing Filter: 'AnonymousAuthenticationFilter'
2020-05-29 10:47:32.514 DEBUG 15316 --- [nio-8080-exec-1] o.s.s.w.a.AnonymousAuthenticationFilter  : Populated SecurityContextHolder with anonymous token: 'org.springframework.security.authentication.AnonymousAuthenticationToken@3bcd7783: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@b364: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: null; Granted Authorities: ROLE_ANONYMOUS'
2020-05-29 10:47:32.514 DEBUG 15316 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy        : /admin/login at position 9 of 11 in additional filter chain; firing Filter: 'SessionManagementFilter'
2020-05-29 10:47:32.514 DEBUG 15316 --- [nio-8080-exec-1] o.s.s.w.session.SessionManagementFilter  : Requested session ID 408BA31320628AA0E4E3E3D9FBEDC8DD is invalid.
2020-05-29 10:47:32.514 DEBUG 15316 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy        : /admin/login at position 10 of 11 in additional filter chain; firing Filter: 'ExceptionTranslationFilter'
2020-05-29 10:47:32.514 DEBUG 15316 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy        : /admin/login at position 11 of 11 in additional filter chain; firing Filter: 'FilterSecurityInterceptor'
2020-05-29 10:47:32.514 DEBUG 15316 --- [nio-8080-exec-1] o.s.s.w.a.i.FilterSecurityInterceptor    : Secure object: FilterInvocation: URL: /admin/login; Attributes: [permitAll]
2020-05-29 10:47:32.514 DEBUG 15316 --- [nio-8080-exec-1] o.s.s.w.a.i.FilterSecurityInterceptor    : Previously Authenticated: org.springframework.security.authentication.AnonymousAuthenticationToken@3bcd7783: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@b364: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: null; Granted Authorities: ROLE_ANONYMOUS
2020-05-29 10:47:32.514 DEBUG 15316 --- [nio-8080-exec-1] o.s.s.access.vote.AffirmativeBased       : Voter: org.springframework.security.web.access.expression.WebExpressionVoter@2cf46161, returned: 1
2020-05-29 10:47:32.514 DEBUG 15316 --- [nio-8080-exec-1] o.s.s.w.a.i.FilterSecurityInterceptor    : Authorization successful
2020-05-29 10:47:32.514 DEBUG 15316 --- [nio-8080-exec-1] o.s.s.w.a.i.FilterSecurityInterceptor    : RunAsManager did not change Authentication object
2020-05-29 10:47:32.514 DEBUG 15316 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy        : /admin/login reached end of additional filter chain; proceeding with original chain
2020-05-29 10:47:32.514 TRACE 15316 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : POST "/admin/login", parameters={masked}, headers={masked} in DispatcherServlet 'dispatcherServlet'
2020-05-29 10:47:32.515 TRACE 15316 --- [nio-8080-exec-1] o.s.b.f.s.DefaultListableBeanFactory     : Returning cached instance of singleton bean 'adminController'
2020-05-29 10:47:32.515 TRACE 15316 --- [nio-8080-exec-1] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to public java.lang.String org.neric.AdminController.processForm(javax.servlet.http.HttpServletRequest,org.neric.bean.Login)
2020-05-29 10:47:32.515 TRACE 15316 --- [nio-8080-exec-1] o.s.web.cors.DefaultCorsProcessor        : Skip: request is from same origin
2020-05-29 10:47:32.516 TRACE 15316 --- [nio-8080-exec-1] .w.s.m.m.a.ServletInvocableHandlerMethod : Arguments: [SecurityContextHolderAwareRequestWrapper[ org.springframework.security.web.context.HttpSessionSecurityContextRepository$Servlet3SaveToSessionRequestWrapper@377af4f5], Login{email='admin@neric.org', password='123456'}]
Login{email='admin@neric.org', password='123456'}
2020-05-29 10:47:32.516 TRACE 15316 --- [nio-8080-exec-1] o.s.w.s.v.InternalResourceViewResolver   : View with key [admin/index] served from cache
2020-05-29 10:47:32.516 DEBUG 15316 --- [nio-8080-exec-1] o.s.w.s.v.ContentNegotiatingViewResolver : Selected 'text/html' given [text/html, application/xhtml+xml, image/webp, image/apng, application/xml;q=0.9, application/signed-exchange;v=b3;q=0.9, */*;q=0.8]
2020-05-29 10:47:32.516 TRACE 15316 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Rendering view [org.thymeleaf.spring5.view.ThymeleafView@6137df47]
2020-05-29 10:47:32.516 TRACE 15316 --- [nio-8080-exec-1] o.s.b.f.s.DefaultListableBeanFactory     : Returning cached instance of singleton bean 'requestDataValueProcessor'
2020-05-29 10:47:32.520 TRACE 15316 --- [nio-8080-exec-1] o.s.b.f.s.DefaultListableBeanFactory     : Returning cached instance of singleton bean 'webSecurityExpressionHandler'
2020-05-29 10:47:32.520 TRACE 15316 --- [nio-8080-exec-1] o.s.b.f.s.DefaultListableBeanFactory     : Returning cached instance of singleton bean 'webSecurityExpressionHandler'
2020-05-29 10:47:32.522 DEBUG 15316 --- [nio-8080-exec-1] o.s.s.w.header.writers.HstsHeaderWriter  : Not injecting HSTS header since it did not match the requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher@5b0bf2c9
2020-05-29 10:47:32.522 DEBUG 15316 --- [nio-8080-exec-1] w.c.HttpSessionSecurityContextRepository : SecurityContext is empty or contents are anonymous - context will not be stored in HttpSession.
2020-05-29 10:47:32.523 DEBUG 15316 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Completed 200 OK, headers={masked}
2020-05-29 10:47:32.523 DEBUG 15316 --- [nio-8080-exec-1] o.s.s.w.a.ExceptionTranslationFilter     : Chain processed normally
2020-05-29 10:47:32.523 DEBUG 15316 --- [nio-8080-exec-1] s.s.w.c.SecurityContextPersistenceFilter : SecurityContextHolder now cleared, as request processing completed
2020-05-29 10:47:32.523 TRACE 15316 --- [nio-8080-exec-1] o.s.b.w.s.f.OrderedRequestContextFilter  : Cleared thread-bound request context: org.apache.catalina.connector.RequestFacade@2b59dd1c
2020-05-29 10:47:32.523 DEBUG 15316 --- [nio-8080-exec-1] o.a.tomcat.util.net.SocketWrapperBase    : Socket: [org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper@77ed1e40:org.apache.tomcat.util.net.NioChannel@78cb69e6:java.nio.channels.SocketChannel[connected local=/0:0:0:0:0:0:0:1:8080 remote=/0:0:0:0:0:0:0:1:50574]], Read from buffer: [0]
2020-05-29 10:47:32.523 DEBUG 15316 --- [nio-8080-exec-1] org.apache.tomcat.util.net.NioEndpoint   : Socket: [org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper@77ed1e40:org.apache.tomcat.util.net.NioChannel@78cb69e6:java.nio.channels.SocketChannel[connected local=/0:0:0:0:0:0:0:1:8080 remote=/0:0:0:0:0:0:0:1:50574]], Read direct from socket: [0]
</code>```

POM

```<code><dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>

    <dependency>
        <groupId>org.thymeleaf.extras</groupId>
        <artifactId>thymeleaf-extras-springsecurity5</artifactId>
        <version>3.0.4.RELEASE</version>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
<dependencies>
</code>```

Application

```<code>@EnableScheduling
@SpringBootApplication
public class Application extends SpringBootServletInitializer {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}
</code>```

SecurityConfig

```<code>@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
            .withUser("user@neric.org")
                .password(passwordEncoder().encode("123456"))
                .roles("USER")
            .and()
                .withUser("admin@neric.org")
                    .password(passwordEncoder().encode("123456"))
                    .roles("ADMIN");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
       http
        .authorizeRequests()
            .antMatchers("/admin/**").hasRole("ADMIN")
        .and()
            .formLogin()
                .loginPage("/admin/login")
                    .usernameParameter("email")
                    .passwordParameter("password")
                .loginProcessingUrl("admin/login") //dont change, it's not the same as the line above.... /smh...
                .failureUrl("/404")
            .permitAll()
        .and()
            .logout()
                .logoutUrl("/admin/logout")
                .invalidateHttpSession(true)
            .permitAll()
        .and()
            .csrf()
                .disable()
        //.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.ALWAYS)
       ;
    }
}
</code>```

WebConfig

```<code>@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/webjars/**", "/img/**", "/css/**", "/js/**")
            .addResourceLocations("classpath:/META-INF/resources/webjars/", "classpath:/static/img/", "classpath:/static/css/", "classpath:/static/js/")
        ;
    }

}
</code>```

AdminController

```<code>@Controller
public class AdminController {

    @GetMapping("/admin/logout")
    public String logout(Login login) {
        return "admin/login";
    }

    @PostMapping("/admin/logout")
    public String logoutPost(Login login) {
        return "admin/login";
    }

    @GetMapping("/admin/login")
    public String login(Login login) {
        return "admin/login";
    }

    @PostMapping("/admin/login")
    public String processForm(HttpServletRequest req, Login login)  {
        System.out.println(login);

        /* If I uncomment this, I can force authentication. Though, I shouldn't have to
        UsernamePasswordAuthenticationToken authReq = new UsernamePasswordAuthenticationToken(login.getEmail(), login.getPassword());
        Authentication auth = authManager.authenticate(authReq);

        SecurityContext sc = SecurityContextHolder.getContext();
        sc.setAuthentication(auth);
        HttpSession session = req.getSession(true);
        session.setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, sc);*/

        return "admin/index";
    }

    @GetMapping("/admin")
    public String showAdmin() {
        return "admin/index";
    }

    @GetMapping("/admin/orgs")
    public String showOrgs(Organization organization) {
        return "admin/orgs";
    }
}
</code>```

/admin/login (/resources/templates/admin/login.html)

```<code><!doctype html>
<html xmlns:th="http://www.thymeleaf.org"
  xmlns:sec="http://www.thymeleaf.org/extras/spring-security"
  lang="en">

<head>
    <link rel="stylesheet" th:href="@{/css/bootstrap.min.css}" />
    <link rel="stylesheet" th:href="@{/css/all.min.css}" />
    <link rel="stylesheet" th:href="@{/css/login.css}" />

</head>
<body>
    <div class="app-container app-theme-white body-tabs-shadow">
        <div class="app-container">
            <div class="h-100 bg-animation">
                <div class="d-flex h-100 justify-content-center align-items-center">
                    <div class="mx-auto app-login-box col-md-8">
                        <div class="app-logo mx-auto mb-3"></div>

                        <form th:action="@{/admin/login}" th:object="${login}" method="post" class="needs-validation" novalidate>
                            <div class="modal-dialog w-100 mx-auto">
                                <div class="modal-content">
                                    <div class="modal-body">
                                        <div class="h5 modal-title text-center">
                                            <h4 class="mt-2">
                                                <div>Welcome back,</div>
                                                <span>Please sign in to your account below.</span>
                                            </h4>
                                        </div>

                                            <div class="form-row">
                                                <div class="col-md-12">
                                                    <div class="position-relative form-group">
                                                        <input name="email" id="exampleEmail" placeholder="Email here..." type="email" class="form-control" th:field="*{email}" required>
                                                    </div>
                                                </div>
                                                <div class="col-md-12">
                                                    <div class="position-relative form-group">
                                                        <input name="password" id="examplePassword" placeholder="Password here..." type="password" class="form-control" th:field="*{password}" required>
                                                    </div>
                                                </div>
                                            </div>
                                    </div>
                                    <div class="modal-footer clearfix">
                                        <div class="float-left">
                                            <a href="javascript:void(0);" class="btn-lg btn btn-link">Recover Password</a> <!--TODO-->
                                        </div>
                                        <div class="float-right">
                                            <button class="btn btn-primary btn-lg" type="submit">Login</button>
                                        </div>
                                    </div>
                                </div>
                            </div>
                        </form>
                        <div class="text-center text-black opacity-8 mt-3" th:text="'Copyright © Capital Region BOCES ' + ${#dates.year(#dates.createNow())}"></div>
                    </div>
                </div>
            </div>
        </div>
    </div>

    <script th:src="@{/js/jquery.min.js}"></script>
    <script th:src="@{/js/bootstrap.bundle.min.js}"></script>
    <script>
        (function() {
            'use strict';

            window.addEventListener('load', function() {
                // Fetch all the forms we want to apply custom Bootstrap validation styles to
                var forms = document.getElementsByClassName('needs-validation');

                // Loop over them and prevent submission
                var validation = Array.prototype.filter.call(forms, function(form) {
                    form.addEventListener('submit', function(event) {
                        if (form.checkValidity() === false) {
                            event.preventDefault();
                            event.stopPropagation();
                        }
                        form.classList.add('was-validated');
                    }, false);
                });
            }, false);
        })();
    </script>

</body>
</code>```

admin/index (/resources/templates/admin/index.html)

Important Part

```<code>    <div sec:authorize="isAuthenticated()">
        Authenticated
    </div>
    <div sec:authorize="!isAuthenticated()">
        Not authenticated
    </div>
</code>```

Entire Page

```<code><!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/extras/spring-security" lang="en">

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <!-- Favicon -->
    <link rel="icon" href="../../assets/img/brand/favicon.png" type="image/png">
    <!-- Fonts -->
    <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Open+Sans:300,400,600,700">
    <!-- Icons -->
    <link rel="stylesheet" th:href="@{/css/nucleo.css}" />
    <link rel="stylesheet" th:href="@{/css/all.min.css}" />
    <!-- Argon CSS -->
    <link rel="stylesheet" th:href="@{/css/argon.css}" />
    <!--<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">-->
</head>

<body class="docs">
<header class="navbar navbar-horizontal navbar-expand navbar-dark flex-row align-items-md-center ct-navbar">
    <a class="navbar-brand mr-0 mr-md-2" th:href="@{/admin/}" aria-label="Admin">
        <img src="https://www.capitalregionboces.org/wp-content/themes/twentyseventeen-child/images/crb-logo-header.svg">
        <sup>Admin</sup>
    </a>
</header>
<div class="container-fluid">
    <div class="row flex-xl-nowrap">
        <div class="col-12 col-md-3 col-xl-2 ct-sidebar">
            <nav class="collapse ct-links" id="ct-docs-nav">
                <!-- Show links for all groups -->
                <div class="ct-toc-item active">
                    <a class="ct-toc-link" href="../../docs/getting-started/overview.html">Manage</a>
                    <ul class="nav ct-sidenav">
                        <li class="active ct-sidenav-active">
                            <a th:href="@{/admin/orgs}">Organizations</a>
                        </li>
                        <li>
                            <a th:href="@{/admin/buildings}">Buildings</a>
                        </li>
                        <li>
                            <a th:href="@{/admin/roles}">Roles</a>
                        </li>
                    </ul>
                </div>
                <!-- Show links for all groups -->
                <div class="ct-toc-item active">
                    <a class="ct-toc-link" href="../../docs/foundation/colors.html">Reports</a>
                    <ul class="nav ct-sidenav">
                        <li>
                            <a th:href="@{/admin/reports/dailyUsage}">Daily Usage</a>
                        </li>
                    </ul>
                </div>

            </nav>
        </div>

        <!-- Main Content: Side Bar -->
        <div class="d-none d-xl-block col-xl-2 ct-toc">
            <ul class="section-nav">
                <li class="toc-entry toc-h3"><a th:href="@{/admin/orgs/add}">Add Organization</a></li>
            </ul>
        </div>

        <main class="col-12 col-md-8 col-xl-7 py-md-3 pl-md-5 ct-content" role="main">
            <!-- Main Content: Header -->
            <div class="ct-page-title">
                <h1 class="ct-title" id="content">Index</h1>
                <div class="avatar-group mt-3"></div>
            </div>
            <!--<p class="ct-lead">A list of all the organizations.</p>-->

            <hr>

            <!-- Main Content -->

            <div sec:authorize="isAuthenticated()">
                Authenticated
            </div>

            <div sec:authorize="!isAuthenticated()">
                Not authenticated
            </div>

            <!-- End Main Content -->

        </main>
    </div>
</div>
<!-- Core -->
<script th:src="@{/js/jquery.min.js}"></script>
<script th:src="@{/js/bootstrap.bundle.min.js}"></script>
<script th:src="@{/js/js.cookie.js}"></script>
<script th:src="@{/js/jquery.scrollbar.min.js}"></script>
<script th:src="@{/js/jquery-scrollLock.min.js}"></script>
<!-- Docs JS -->
<script th:src="@{/js/anchor.min.js}"></script>
<script th:src="@{/js/clipboard.min.js}"></script>
<script th:src="@{/js/holder.min.js}"></script>
<script th:src="@{/js/prism.js}"></script>
<!-- Argon JS -->
<script th:src="@{/js/argon.min.js}"></script>
</body>

</html>
</code>```

答:
The issue is in your security config.

You are protecting everything. See below.

```<code>http
    .authorizeRequests()
        .antMatchers("/admin/**").hasRole("ADMIN")
</code>```

So it will keep you on sending the login page again and again. Because it didn't pass security filters successfully.

I would suggest changing your controller mapping to @GetMapping("/login") and permit this to be accessible to everyone by adding below config changes

```<code> @Override
protected void configure(HttpSecurity http) throws Exception {
   http
    .authorizeRequests().antMatchers("/login").permitAll()
        .antMatchers("/admin/**").hasRole("ADMIN")
    .and()
        .formLogin().loginPage("/login") // cutom login page.
        .usernameParameter("email")
        .passwordParameter("password")
        .loginProcessingUrl("/login")  // this should be public too(which we did) as user's request would reach here then authentication will happen.
        .defaultSuccessUrl("/loginSuccess") // this has to be done since you have two different role so we need to identify whether user role or admin role and based on that redirection should happen.
    .and()
        .logout()
            .logoutUrl("/logout") // logout should be common for both roles so don't add /admin/logout
             .invalidateHttpSession(true)
        .permitAll()
    .and()
        .csrf()
            .disable()
    //.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.ALWAYS)
   ;
}
</code>```

Here is the controller :

```<code>@Controller
public class AdminController {
@GetMapping("/login")
public String login() {
    return "admin/login";
}
@GetMapping("/loginSuccess")
public String showAdmin(@AuthenticationPrincipal User userDetails) {
    // check the role if user then redirect to user page, if admin then redirect to admin page.
    List<GrantedAuthority> authorties = userDetails.getAuthorities().stream().collect(Collectors.toList());

    boolean isAdmin=false;
    if(authorties.stream().anyMatch(p->p.getAuthority().equals("ROLE_ADMIN"))) {
        return "admin/index";
    } else {
        return "user/index";
    }
}
</code>```

}

Checkout working code here

Body:
I'm having trouble figuring out why I am unable to authenticate. After looking at the trace I think the session isn't being created, or is cleared, or something of that nature. There is a block of code in my Admin Controller login post method that if uncommented the authentication works, however I don't want to manually do this, as Spring should handle it for me. Any help would be appreciated, and if I can include additional source code to help, let me know.

Details:

I am using Spring Boot, Spring Security, Thymleaf and running this through Localhost.

Every time I view a page on /admin/** the login page appears and I need to provide credentials. When the credentials are entered, the login page processes and displays /admin/index (as per the controller), however when I try navigating to another page (say: /admin/orgs) the login screen reappears. Aside from the obvious, I know that the user is not being authenticated as I check if the user is authenticated on /admin/index.

Project Structure

+ src
    + main
        + java
            - org.neric (package)
                - SecurityConfig
                - AdminController
                - AppController
                - Application
                - WebConfig
        + resources
            + templates
                - index.html
                + admin
                    - index.html
                    - login.html
                    - orgs.html
            + static
                + css
                + js
                + img

Trace (application.properties trace=true)

2020-05-29 10:47:32.511 DEBUG 15316 --- [nio-8080-exec-1] o.a.tomcat.util.net.SocketWrapperBase    : Socket: [org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper@77ed1e40:org.apache.tomcat.util.net.NioChannel@78cb69e6:java.nio.channels.SocketChannel[connected local=/0:0:0:0:0:0:0:1:8080 remote=/0:0:0:0:0:0:0:1:50574]], Read from buffer: [0]
2020-05-29 10:47:32.511 DEBUG 15316 --- [nio-8080-exec-1] org.apache.tomcat.util.net.NioEndpoint   : Socket: [org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper@77ed1e40:org.apache.tomcat.util.net.NioChannel@78cb69e6:java.nio.channels.SocketChannel[connected local=/0:0:0:0:0:0:0:1:8080 remote=/0:0:0:0:0:0:0:1:50574]], Read direct from socket: [872]
2020-05-29 10:47:32.512 DEBUG 15316 --- [nio-8080-exec-1] o.a.t.util.http.Rfc6265CookieProcessor   : Cookies: Parsing b[]: sidenav-state=pinned; JSESSIONID=408BA31320628AA0E4E3E3D9FBEDC8DD
2020-05-29 10:47:32.512 DEBUG 15316 --- [nio-8080-exec-1] o.a.catalina.connector.CoyoteAdapter     :  Requested cookie session id is 408BA31320628AA0E4E3E3D9FBEDC8DD
2020-05-29 10:47:32.512 DEBUG 15316 --- [nio-8080-exec-1] o.a.c.authenticator.AuthenticatorBase    : Security checking request POST /admin/login
2020-05-29 10:47:32.512 DEBUG 15316 --- [nio-8080-exec-1] org.apache.catalina.realm.RealmBase      :   No applicable constraints defined
2020-05-29 10:47:32.512 DEBUG 15316 --- [nio-8080-exec-1] o.a.c.authenticator.AuthenticatorBase    :  Not subject to any constraint
2020-05-29 10:47:32.512 DEBUG 15316 --- [nio-8080-exec-1] o.apache.catalina.core.StandardWrapper   :   Returning non-STM instance
2020-05-29 10:47:32.513 DEBUG 15316 --- [nio-8080-exec-1] org.apache.tomcat.util.http.Parameters   : Set encoding to UTF-8
2020-05-29 10:47:32.513 DEBUG 15316 --- [nio-8080-exec-1] org.apache.tomcat.util.http.Parameters   : Start processing with input [%24%7B_csrf.parameterName%7D=%24%7B_csrf.token%7D&email=admin%40neric.org&password=123456]
2020-05-29 10:47:32.513 TRACE 15316 --- [nio-8080-exec-1] o.s.b.w.s.f.OrderedRequestContextFilter  : Bound request context to thread: org.apache.catalina.connector.RequestFacade@2b59dd1c
2020-05-29 10:47:32.513 DEBUG 15316 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy        : /admin/login at position 1 of 11 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
2020-05-29 10:47:32.513 DEBUG 15316 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy        : /admin/login at position 2 of 11 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
2020-05-29 10:47:32.513 DEBUG 15316 --- [nio-8080-exec-1] w.c.HttpSessionSecurityContextRepository : No HttpSession currently exists
2020-05-29 10:47:32.513 DEBUG 15316 --- [nio-8080-exec-1] w.c.HttpSessionSecurityContextRepository : No SecurityContext was available from the HttpSession: null. A new one will be created.
2020-05-29 10:47:32.513 DEBUG 15316 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy        : /admin/login at position 3 of 11 in additional filter chain; firing Filter: 'HeaderWriterFilter'
2020-05-29 10:47:32.513 DEBUG 15316 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy        : /admin/login at position 4 of 11 in additional filter chain; firing Filter: 'LogoutFilter'
2020-05-29 10:47:32.513 DEBUG 15316 --- [nio-8080-exec-1] o.s.s.web.util.matcher.OrRequestMatcher  : Trying to match using Ant [pattern='/admin/logout', GET]
2020-05-29 10:47:32.513 DEBUG 15316 --- [nio-8080-exec-1] o.s.s.w.u.matcher.AntPathRequestMatcher  : Request 'POST /admin/login' doesn't match 'GET /admin/logout'
2020-05-29 10:47:32.513 DEBUG 15316 --- [nio-8080-exec-1] o.s.s.web.util.matcher.OrRequestMatcher  : Trying to match using Ant [pattern='/admin/logout', POST]
2020-05-29 10:47:32.513 DEBUG 15316 --- [nio-8080-exec-1] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/admin/login'; against '/admin/logout'
2020-05-29 10:47:32.513 DEBUG 15316 --- [nio-8080-exec-1] o.s.s.web.util.matcher.OrRequestMatcher  : Trying to match using Ant [pattern='/admin/logout', PUT]
2020-05-29 10:47:32.513 DEBUG 15316 --- [nio-8080-exec-1] o.s.s.w.u.matcher.AntPathRequestMatcher  : Request 'POST /admin/login' doesn't match 'PUT /admin/logout'
2020-05-29 10:47:32.513 DEBUG 15316 --- [nio-8080-exec-1] o.s.s.web.util.matcher.OrRequestMatcher  : Trying to match using Ant [pattern='/admin/logout', DELETE]
2020-05-29 10:47:32.513 DEBUG 15316 --- [nio-8080-exec-1] o.s.s.w.u.matcher.AntPathRequestMatcher  : Request 'POST /admin/login' doesn't match 'DELETE /admin/logout'
2020-05-29 10:47:32.514 DEBUG 15316 --- [nio-8080-exec-1] o.s.s.web.util.matcher.OrRequestMatcher  : No matches found
2020-05-29 10:47:32.514 DEBUG 15316 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy        : /admin/login at position 5 of 11 in additional filter chain; firing Filter: 'UsernamePasswordAuthenticationFilter'
2020-05-29 10:47:32.514 DEBUG 15316 --- [nio-8080-exec-1] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/admin/login'; against 'admin/login'
2020-05-29 10:47:32.514 DEBUG 15316 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy        : /admin/login at position 6 of 11 in additional filter chain; firing Filter: 'RequestCacheAwareFilter'
2020-05-29 10:47:32.514 DEBUG 15316 --- [nio-8080-exec-1] o.s.s.w.s.HttpSessionRequestCache        : saved request doesn't match
2020-05-29 10:47:32.514 DEBUG 15316 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy        : /admin/login at position 7 of 11 in additional filter chain; firing Filter: 'SecurityContextHolderAwareRequestFilter'
2020-05-29 10:47:32.514 DEBUG 15316 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy        : /admin/login at position 8 of 11 in additional filter chain; firing Filter: 'AnonymousAuthenticationFilter'
2020-05-29 10:47:32.514 DEBUG 15316 --- [nio-8080-exec-1] o.s.s.w.a.AnonymousAuthenticationFilter  : Populated SecurityContextHolder with anonymous token: 'org.springframework.security.authentication.AnonymousAuthenticationToken@3bcd7783: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@b364: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: null; Granted Authorities: ROLE_ANONYMOUS'
2020-05-29 10:47:32.514 DEBUG 15316 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy        : /admin/login at position 9 of 11 in additional filter chain; firing Filter: 'SessionManagementFilter'
2020-05-29 10:47:32.514 DEBUG 15316 --- [nio-8080-exec-1] o.s.s.w.session.SessionManagementFilter  : Requested session ID 408BA31320628AA0E4E3E3D9FBEDC8DD is invalid.
2020-05-29 10:47:32.514 DEBUG 15316 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy        : /admin/login at position 10 of 11 in additional filter chain; firing Filter: 'ExceptionTranslationFilter'
2020-05-29 10:47:32.514 DEBUG 15316 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy        : /admin/login at position 11 of 11 in additional filter chain; firing Filter: 'FilterSecurityInterceptor'
2020-05-29 10:47:32.514 DEBUG 15316 --- [nio-8080-exec-1] o.s.s.w.a.i.FilterSecurityInterceptor    : Secure object: FilterInvocation: URL: /admin/login; Attributes: [permitAll]
2020-05-29 10:47:32.514 DEBUG 15316 --- [nio-8080-exec-1] o.s.s.w.a.i.FilterSecurityInterceptor    : Previously Authenticated: org.springframework.security.authentication.AnonymousAuthenticationToken@3bcd7783: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@b364: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: null; Granted Authorities: ROLE_ANONYMOUS
2020-05-29 10:47:32.514 DEBUG 15316 --- [nio-8080-exec-1] o.s.s.access.vote.AffirmativeBased       : Voter: org.springframework.security.web.access.expression.WebExpressionVoter@2cf46161, returned: 1
2020-05-29 10:47:32.514 DEBUG 15316 --- [nio-8080-exec-1] o.s.s.w.a.i.FilterSecurityInterceptor    : Authorization successful
2020-05-29 10:47:32.514 DEBUG 15316 --- [nio-8080-exec-1] o.s.s.w.a.i.FilterSecurityInterceptor    : RunAsManager did not change Authentication object
2020-05-29 10:47:32.514 DEBUG 15316 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy        : /admin/login reached end of additional filter chain; proceeding with original chain
2020-05-29 10:47:32.514 TRACE 15316 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : POST "/admin/login", parameters={masked}, headers={masked} in DispatcherServlet 'dispatcherServlet'
2020-05-29 10:47:32.515 TRACE 15316 --- [nio-8080-exec-1] o.s.b.f.s.DefaultListableBeanFactory     : Returning cached instance of singleton bean 'adminController'
2020-05-29 10:47:32.515 TRACE 15316 --- [nio-8080-exec-1] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to public java.lang.String org.neric.AdminController.processForm(javax.servlet.http.HttpServletRequest,org.neric.bean.Login)
2020-05-29 10:47:32.515 TRACE 15316 --- [nio-8080-exec-1] o.s.web.cors.DefaultCorsProcessor        : Skip: request is from same origin
2020-05-29 10:47:32.516 TRACE 15316 --- [nio-8080-exec-1] .w.s.m.m.a.ServletInvocableHandlerMethod : Arguments: [SecurityContextHolderAwareRequestWrapper[ org.springframework.security.web.context.HttpSessionSecurityContextRepository$Servlet3SaveToSessionRequestWrapper@377af4f5], Login{email='admin@neric.org', password='123456'}]
Login{email='admin@neric.org', password='123456'}
2020-05-29 10:47:32.516 TRACE 15316 --- [nio-8080-exec-1] o.s.w.s.v.InternalResourceViewResolver   : View with key [admin/index] served from cache
2020-05-29 10:47:32.516 DEBUG 15316 --- [nio-8080-exec-1] o.s.w.s.v.ContentNegotiatingViewResolver : Selected 'text/html' given [text/html, application/xhtml+xml, image/webp, image/apng, application/xml;q=0.9, application/signed-exchange;v=b3;q=0.9, */*;q=0.8]
2020-05-29 10:47:32.516 TRACE 15316 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Rendering view [org.thymeleaf.spring5.view.ThymeleafView@6137df47]
2020-05-29 10:47:32.516 TRACE 15316 --- [nio-8080-exec-1] o.s.b.f.s.DefaultListableBeanFactory     : Returning cached instance of singleton bean 'requestDataValueProcessor'
2020-05-29 10:47:32.520 TRACE 15316 --- [nio-8080-exec-1] o.s.b.f.s.DefaultListableBeanFactory     : Returning cached instance of singleton bean 'webSecurityExpressionHandler'
2020-05-29 10:47:32.520 TRACE 15316 --- [nio-8080-exec-1] o.s.b.f.s.DefaultListableBeanFactory     : Returning cached instance of singleton bean 'webSecurityExpressionHandler'
2020-05-29 10:47:32.522 DEBUG 15316 --- [nio-8080-exec-1] o.s.s.w.header.writers.HstsHeaderWriter  : Not injecting HSTS header since it did not match the requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher@5b0bf2c9
2020-05-29 10:47:32.522 DEBUG 15316 --- [nio-8080-exec-1] w.c.HttpSessionSecurityContextRepository : SecurityContext is empty or contents are anonymous - context will not be stored in HttpSession.
2020-05-29 10:47:32.523 DEBUG 15316 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Completed 200 OK, headers={masked}
2020-05-29 10:47:32.523 DEBUG 15316 --- [nio-8080-exec-1] o.s.s.w.a.ExceptionTranslationFilter     : Chain processed normally
2020-05-29 10:47:32.523 DEBUG 15316 --- [nio-8080-exec-1] s.s.w.c.SecurityContextPersistenceFilter : SecurityContextHolder now cleared, as request processing completed
2020-05-29 10:47:32.523 TRACE 15316 --- [nio-8080-exec-1] o.s.b.w.s.f.OrderedRequestContextFilter  : Cleared thread-bound request context: org.apache.catalina.connector.RequestFacade@2b59dd1c
2020-05-29 10:47:32.523 DEBUG 15316 --- [nio-8080-exec-1] o.a.tomcat.util.net.SocketWrapperBase    : Socket: [org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper@77ed1e40:org.apache.tomcat.util.net.NioChannel@78cb69e6:java.nio.channels.SocketChannel[connected local=/0:0:0:0:0:0:0:1:8080 remote=/0:0:0:0:0:0:0:1:50574]], Read from buffer: [0]
2020-05-29 10:47:32.523 DEBUG 15316 --- [nio-8080-exec-1] org.apache.tomcat.util.net.NioEndpoint   : Socket: [org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper@77ed1e40:org.apache.tomcat.util.net.NioChannel@78cb69e6:java.nio.channels.SocketChannel[connected local=/0:0:0:0:0:0:0:1:8080 remote=/0:0:0:0:0:0:0:1:50574]], Read direct from socket: [0]

POM

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>

    <dependency>
        <groupId>org.thymeleaf.extras</groupId>
        <artifactId>thymeleaf-extras-springsecurity5</artifactId>
        <version>3.0.4.RELEASE</version>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
<dependencies>

Application

@EnableScheduling
@SpringBootApplication
public class Application extends SpringBootServletInitializer {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

SecurityConfig

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
            .withUser("user@neric.org")
                .password(passwordEncoder().encode("123456"))
                .roles("USER")
            .and()
                .withUser("admin@neric.org")
                    .password(passwordEncoder().encode("123456"))
                    .roles("ADMIN");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
       http
        .authorizeRequests()
            .antMatchers("/admin/**").hasRole("ADMIN")
        .and()
            .formLogin()
                .loginPage("/admin/login")
                    .usernameParameter("email")
                    .passwordParameter("password")
                .loginProcessingUrl("admin/login") //dont change, it's not the same as the line above.... /smh...
                .failureUrl("/404")
            .permitAll()
        .and()
            .logout()
                .logoutUrl("/admin/logout")
                .invalidateHttpSession(true)
            .permitAll()
        .and()
            .csrf()
                .disable()
        //.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.ALWAYS)
       ;
    }
}

WebConfig

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/webjars/**", "/img/**", "/css/**", "/js/**")
            .addResourceLocations("classpath:/META-INF/resources/webjars/", "classpath:/static/img/", "classpath:/static/css/", "classpath:/static/js/")
        ;
    }

}

AdminController

@Controller
public class AdminController {

    @GetMapping("/admin/logout")
    public String logout(Login login) {
        return "admin/login";
    }

    @PostMapping("/admin/logout")
    public String logoutPost(Login login) {
        return "admin/login";
    }

    @GetMapping("/admin/login")
    public String login(Login login) {
        return "admin/login";
    }

    @PostMapping("/admin/login")
    public String processForm(HttpServletRequest req, Login login)  {
        System.out.println(login);

        /* If I uncomment this, I can force authentication. Though, I shouldn't have to
        UsernamePasswordAuthenticationToken authReq = new UsernamePasswordAuthenticationToken(login.getEmail(), login.getPassword());
        Authentication auth = authManager.authenticate(authReq);

        SecurityContext sc = SecurityContextHolder.getContext();
        sc.setAuthentication(auth);
        HttpSession session = req.getSession(true);
        session.setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, sc);*/

        return "admin/index";
    }

    @GetMapping("/admin")
    public String showAdmin() {
        return "admin/index";
    }

    @GetMapping("/admin/orgs")
    public String showOrgs(Organization organization) {
        return "admin/orgs";
    }
}

/admin/login (/resources/templates/admin/login.html)

<!doctype html>

    <link rel="stylesheet" th:href="@{/css/bootstrap.min.css}" />
    <link rel="stylesheet" th:href="@{/css/all.min.css}" />
    <link rel="stylesheet" th:href="@{/css/login.css}" />

                        <form th:action="@{/admin/login}" th:object="${login}" method="post" class="needs-validation" novalidate>

Welcome back,

                                                Please sign in to your account below.

                                                        <input name="email" id="exampleEmail" placeholder="Email here..." type="email" class="form-control" th:field="*{email}" required>

                                                        <input name="password" id="examplePassword" placeholder="Password here..." type="password" class="form-control" th:field="*{password}" required>

                                            Recover Password

                                            <button class="btn btn-primary btn-lg" type="submit">Login</button>

                        </form>

admin/index (/resources/templates/admin/index.html)

Important Part

        Authenticated

        Not authenticated

Entire Page

<!DOCTYPE html>

    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <link rel="icon" href="../../assets/img/brand/favicon.png" type="image/png">

    <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Open+Sans:300,400,600,700">

    <link rel="stylesheet" th:href="@{/css/nucleo.css}" />
    <link rel="stylesheet" th:href="@{/css/all.min.css}" />

    <link rel="stylesheet" th:href="@{/css/argon.css}" />

        Admin

                    Manage

                            Organizations

                            Buildings

                            Roles

                    Reports

                            Daily Usage

Add Organization

Index

                Authenticated

                Not authenticated

Body:
The issue is in your security config.

You are protecting everything. See below.

http
    .authorizeRequests()
        .antMatchers("/admin/**").hasRole("ADMIN")

So it will keep you on sending the login page again and again. Because it didn't pass security filters successfully.

I would suggest changing your controller mapping to @GetMapping("/login") and permit this to be accessible to everyone by adding below config changes

 @Override
protected void configure(HttpSecurity http) throws Exception {
   http
    .authorizeRequests().antMatchers("/login").permitAll()
        .antMatchers("/admin/**").hasRole("ADMIN")
    .and()
        .formLogin().loginPage("/login") // cutom login page.
        .usernameParameter("email")
        .passwordParameter("password")
        .loginProcessingUrl("/login")  // this should be public too(which we did) as user's request would reach here then authentication will happen.
        .defaultSuccessUrl("/loginSuccess") // this has to be done since you have two different role so we need to identify whether user role or admin role and based on that redirection should happen.
    .and()
        .logout()
            .logoutUrl("/logout") // logout should be common for both roles so don't add /admin/logout
             .invalidateHttpSession(true)
        .permitAll()
    .and()
        .csrf()
            .disable()
    //.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.ALWAYS)
   ;
}

Here is the controller :

@Controller
public class AdminController {
@GetMapping("/login")
public String login() {
    return "admin/login";
}
@GetMapping("/loginSuccess")
public String showAdmin(@AuthenticationPrincipal User userDetails) {
    // check the role if user then redirect to user page, if admin then redirect to admin page.
    List<GrantedAuthority> authorties = userDetails.getAuthorities().stream().collect(Collectors.toList());

    boolean isAdmin=false;
    if(authorties.stream().anyMatch(p->p.getAuthority().equals("ROLE_ADMIN"))) {
        return "admin/index";
    } else {
        return "user/index";
    }
}

}

Checkout working code here


来源: stackexchange

问:
NodeJs MongoDB how to make a complex GET request
I'm writing a node js method which I want to find fields which the imei.name parameters is passed in link and the test field is empty that's the method which I tried to use:

```<code> router.get('/gettdata/', (req, res, next) => {

        let aa=req.query.imei;
        console.log(aa);
        Post.findOne( {imei:{"imei.name":req.body},
      test:{$exists:false}
      }

        )
          .then((posts) => {
            res.json(posts);
            console.log(posts);
          })
          .catch(err => console.log(err))
      });
</code>```

it returns an empty field despite it exists when I use only

```<code>Post.findOne( {"imei.name":req.body})
</code>```

it works and when I use only

```<code>Post.findOne(
  test:{$exists:false}
 )
</code>```

it works but the problem is using them two in one time

```<code>{
        "_id" : ObjectId("5eb7f90fb88d8d4d94184333"),
        "imei" : {
                "name" : "bokhhjbr",
                "modele" : "5486"
        },
        "__v" : 0
}
{
        "_id" : ObjectId("5eb7fb9cb88d8d4d94184334"),
        "imei" : {
                "name" : "865566048694354",
                "modele" : "CPH1803"
        },
        "__v" : 0
}
{
        "_id" : ObjectId("5eba73395fd8dd073065efdf"),
        "imei" : {
                "name" : "bok55555555555hjbr",
                "modele" : "5486"
        },
        "createdAt" : ISODate("2020-05-12T09:58:17.203Z"),
        "updatedAt" : ISODate("2020-05-12T09:58:17.203Z"),
        "__v" : 0
}
{
        "_id" : ObjectId("5ed0f93c0c93ec1c785358ab"),
        "imei" : {
                "name" : "865566048694354"
        },
        "test" : {
                "tactileState" : "1",
                "pixelState" : "1",
                "greyState" : "1"
        },
</code>```

答:
Since you are using the GET method, the value of req.body will be {} object.

That's why Post.findOne( {"imei.name":req.body}) returns null

You either pass the parameter as either request params of query params. If you are passing it as request params then you GET call would be like

```<code>http://localhost:3000/post/gettdata?name=865566048694354
</code>```

Now you can access the name in your method like

router.get("/gettdata", (req, res) => {
  let name = req.query.name;
  Post.findOne({'imei.name': name, test: { $exists: false } })
    .then((posts) => {
      res.json(posts);
      console.log(posts);
    })
    .catch(err => {
      console.log(err)
      res.status(500).json({ message: "Error" })
  });
});

```

Also, there was an issue in the syntax for querying nested object, should specify the field and nested field in dot notation 'field.nestedField'. I have updated it in the above function.

ContentLicense: CC BY-SA 4.0

Body:
I'm writing a node js method which I want to find fields which the imei.name parameters is passed in link and the test field is empty that's the method which I tried to use:

 router.get('/gettdata/', (req, res, next) => {

        let aa=req.query.imei;
        console.log(aa);
        Post.findOne( {imei:{"imei.name":req.body},
      test:{$exists:false}
      }

        )
          .then((posts) => {
            res.json(posts);
            console.log(posts);
          })
          .catch(err => console.log(err))
      });

it returns an empty field despite it exists when I use only

Post.findOne( {"imei.name":req.body})

it works and when I use only

Post.findOne(
  test:{$exists:false}
 )

it works but the problem is using them two in one time

{
        "_id" : ObjectId("5eb7f90fb88d8d4d94184333"),
        "imei" : {
                "name" : "bokhhjbr",
                "modele" : "5486"
        },
        "__v" : 0
}
{
        "_id" : ObjectId("5eb7fb9cb88d8d4d94184334"),
        "imei" : {
                "name" : "865566048694354",
                "modele" : "CPH1803"
        },
        "__v" : 0
}
{
        "_id" : ObjectId("5eba73395fd8dd073065efdf"),
        "imei" : {
                "name" : "bok55555555555hjbr",
                "modele" : "5486"
        },
        "createdAt" : ISODate("2020-05-12T09:58:17.203Z"),
        "updatedAt" : ISODate("2020-05-12T09:58:17.203Z"),
        "__v" : 0
}
{
        "_id" : ObjectId("5ed0f93c0c93ec1c785358ab"),
        "imei" : {
                "name" : "865566048694354"
        },
        "test" : {
                "tactileState" : "1",
                "pixelState" : "1",
                "greyState" : "1"
        },

Body:
Since you are using the GET method, the value of req.body will be {} object.

That's why Post.findOne( {"imei.name":req.body}) returns null

You either pass the parameter as either request params of query params. If you are passing it as request params then you GET call would be like

http://localhost:3000/post/gettdata?name=865566048694354

Now you can access the name in your method like

router.get("/gettdata", (req, res) => {
  let name = req.query.name;
  Post.findOne({'imei.name': name, test: { $exists: false } })
    .then((posts) => {
      res.json(posts);
      console.log(posts);
    })
    .catch(err => {
      console.log(err)
      res.status(500).json({ message: "Error" })
  });
});

Also, there was an issue in the syntax for querying nested object, should specify the field and nested field in dot notation 'field.nestedField'. I have updated it in the above function.


问:
How to hide navigationBar when scrolling collectionView in ios 11?
In iOS 11 searchBar hides when scrolling tableView if we set to hide. how can I hide searchBar, navigationBar and tabBar when scrolling collectionView up ? And unhide them all when scrolling down ? Thanks for your help...</p>

答:
Translating KTang's accepted answer to Objective-C:</p>

```<code>- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
    UIPanGestureRecognizer *pan = scrollView.panGestureRecognizer;
    CGFloat velocity = [pan velocityInView:scrollView].y;

    if (velocity < -5) {
        [self.navigationController setNavigationBarHidden:YES animated:YES];
        [self.navigationController setToolbarHidden:YES animated:YES];
    } else if (velocity > 5) {
        [self.navigationController setNavigationBarHidden:YES animated:YES];
        [self.navigationController setToolbarHidden:YES animated:YES];
    }
}
</code>```

ContentLicense: CC BY-SA 3.0

Body:
Translating KTang's accepted answer to Objective-C:

- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
    UIPanGestureRecognizer *pan = scrollView.panGestureRecognizer;
    CGFloat velocity = [pan velocityInView:scrollView].y;

    if (velocity < -5) {
        [self.navigationController setNavigationBarHidden:YES animated:YES];
        [self.navigationController setToolbarHidden:YES animated:YES];
    } else if (velocity > 5) {
        [self.navigationController setNavigationBarHidden:YES animated:YES];
        [self.navigationController setToolbarHidden:YES animated:YES];
    }
}


问:
Get rootDir inside beforeSend function in Symfony to intercept Sentry event
I have a service who intercepts the events of Sentry. I'm using a function called beforeSend.

I would to load a json file who contains the data to scrub or to keep. It's a service and I build my constructor with a similar way than others, but the "$this" context doesn't exist when I'm in the debugger in this function.

The kernel is in the Global variables, but I think it's not a good idea... I only would to get the root dir and it's all, but I don't find how to do this in this class...  The constructor seems useless.

Someone could help me with a similar experience ?

EDIT :

Service :

```<code>namespace App\Services;

use Sentry\Event;
use Symfony\Component\HttpKernel\KernelInterface;

class SentryBeforeSendService
{
private static $rootDir;

public function __construct(KernelInterface $kernel)
{
    self::$rootDir = $kernel->getRootDir();
}

/**
 * Scrubs the value of all TARGET_PARAMETERS
 * in the event's request.
 *
 * @param Event $event
 *
 * @return Event
 */
public function beforeSend(Event $event)
{
    $rootDir = self::$rootDir;
    $event->setRequest(self::scrubRequest($event->getRequest(), $rootDir));
    try {
        $composerData = json_decode(file_get_contents($rootDir.'/../composer.json'), true);
        $version      = $composerData['version'];
        $event->setRelease($version);
    } catch (\Exception $e) {
        //do nothing
    }

    return $event;
}

/**
 * Scrubs GET and POST parameters
 *
 * @param array $request
 *
 * @return array
 */
private static function scrubRequest(array $request, $rootDir)
{
    // DO SOMETHING WITH $rootDir to scrub data with external file
}}
</code>```

services.yml :

```<code> app.service.sentry_before_send:
     class: 'App\Services\SentryBeforeSendService'
     arguments: ['@kernel']
</code>```

config_prod.yml :

```<code>sentry:
    dsn: "%sentry_dsn%"
    options:
        environment: "%sentry_environment%"
        # release: '%env(VERSION)%' #overridden from composer.json version in SentryBeforeSendService::beforeSend
        before_send: 'App\Services\SentryBeforeSendService::beforeSend'
</code>```

But it seems the construct never happened.

Thank you very much.

答:
You can inject the kernel.project_dir parameter in your service constructor with a named parameter:

In your services.yml file:

```<code>services:
    _defaults:
        bind:
            string $kernelProjectDir: '%kernel.project_dir%'
</code>```

Then in your service:

```<code>public function __construct(string $kernelProjectDir)
{
</code>```

Body:
I have a service who intercepts the events of Sentry. I'm using a function called beforeSend.

I would to load a json file who contains the data to scrub or to keep. It's a service and I build my constructor with a similar way than others, but the "$this" context doesn't exist when I'm in the debugger in this function.

The kernel is in the Global variables, but I think it's not a good idea... I only would to get the root dir and it's all, but I don't find how to do this in this class...  The constructor seems useless.

Someone could help me with a similar experience ?

EDIT :

Service :

namespace App\Services;

use Sentry\Event;
use Symfony\Component\HttpKernel\KernelInterface;

class SentryBeforeSendService
{
private static $rootDir;

public function __construct(KernelInterface $kernel)
{
    self::$rootDir = $kernel->getRootDir();
}

/**
 * Scrubs the value of all TARGET_PARAMETERS
 * in the event's request.
 *
 * @param Event $event
 *
 * @return Event
 */
public function beforeSend(Event $event)
{
    $rootDir = self::$rootDir;
    $event->setRequest(self::scrubRequest($event->getRequest(), $rootDir));
    try {
        $composerData = json_decode(file_get_contents($rootDir.'/../composer.json'), true);
        $version      = $composerData['version'];
        $event->setRelease($version);
    } catch (\Exception $e) {
        //do nothing
    }

    return $event;
}

/**
 * Scrubs GET and POST parameters
 *
 * @param array $request
 *
 * @return array
 */
private static function scrubRequest(array $request, $rootDir)
{
    // DO SOMETHING WITH $rootDir to scrub data with external file
}}

services.yml :

 app.service.sentry_before_send:
     class: 'App\Services\SentryBeforeSendService'
     arguments: ['@kernel']

config_prod.yml :

sentry:
    dsn: "%sentry_dsn%"
    options:
        environment: "%sentry_environment%"
        # release: '%env(VERSION)%' #overridden from composer.json version in SentryBeforeSendService::beforeSend
        before_send: 'App\Services\SentryBeforeSendService::beforeSend'

But it seems the construct never happened.

Thank you very much.

Body:
You can inject the kernel.project_dir parameter in your service constructor with a named parameter:

In your services.yml file:

services:
    _defaults:
        bind:
            string $kernelProjectDir: '%kernel.project_dir%'

Then in your service:

public function __construct(string $kernelProjectDir)
{


问:
Is there any way to load woo-commerce classes before the form submit action function
I have created a function to add products to the woo-commerce programmatically and tested it on admin_init hook and it is working fine. But when I am running the same function using form submit action it is showing the class not found errors as till now woo-commerce class are not loaded.

I have manually included few classes but in those classes itself some constant and other classes are used which comes with woo-commerce and causing the same fatal error.

Is there any way so that I can load all the classes before my function start executing.

答:
Function is working fine when it is moved from custom plugin to function.php.
Reason is WordPress load plugins before the theme files. </p>


问:
Why code not working for configuring while loop
Why my code not working

a “game” where, through input, enter the number of players who will play the game, how to make so many inputs (for player nicknames) be displayed, how many players are playing? and how to do something to add all the nicknames to the array later?
For example: the host introduced what 5 people play, how to output 5 inputs and then add all the nicknames to the array?

```<code>print("Add players!")
plam = int(input("How many players will be? \n"))

playersNames = []
i = len(playersNames)
while i < plam:
    writePlayerName = input("Write player's nickname:   " + Fore.GREEN)
    print(Fore.WHITE)
    playersNames.append(writePlayerName)
    print(playersNames)
    break;
</code>```

Problem statement

player names can be entered endlessly, but I here restricted that there will be 5 players

答: Your break; statement is ending the while loop. You can remove the while loop and it should function as as you meant it to.

Body:
Why my code not working

a “game” where, through input, enter the number of players who will play the game, how to make so many inputs (for player nicknames) be displayed, how many players are playing? and how to do something to add all the nicknames to the array later?
For example: the host introduced what 5 people play, how to output 5 inputs and then add all the nicknames to the array?

print("Add players!")
plam = int(input("How many players will be? \n"))

playersNames = []
i = len(playersNames)
while i < plam:
    writePlayerName = input("Write player's nickname:   " + Fore.GREEN)
    print(Fore.WHITE)
    playersNames.append(writePlayerName)
    print(playersNames)
    break;

Problem statement

player names can be entered endlessly, but I here restricted that there will be 5 players


问:
How to get a Tracking_ID in Google Analytics for iOS apps for Google Tag Manager usage?
I've set up my account in Google Analytics, Google Tag Manager and have already installed firebase/analytics and GoogleTagManager to my project...

The problem is when I go the admin section and click on the default property created for my app I have no Tracking_ID on property settings menu,
the property is set as app + web I've tried to set a new property as App it then gives me only a .plist and no Tracking_ID for managing the events on Google Tag Manager...

Is google tag manager a tool meant to be used only for web now?
Because I installed it on iOS but it seems GA doesn't provide Tracking_ID for mobile tracking and without Tracking_ID I just can't use GTM...

Any help on this matter would be highly appreciated,
Thank you all in advance)

答:
GA App properties were sunsetted and are now app+web properties.

In GA for the Property you created, you also need to add a data stream. App+Web Properties use a Measurement ID, not a Tracking ID.

When you add an app data stream, Analytics creates a corresponding
  Firebase project and app data stream, and automatically links the
  Firebase project to your property if your project and property are not
  already linked. Currently, you cannot link to an existing Firebase
  project from Analytics.

Enter the iOS bundle ID or Android package name, the app name, and for
  iOS, the App Store ID, then click Register app.

Analytics will progress through the steps of creating a Google Cloud
  project, a Firebase project, linking the Firebase project to
  Analytics, and creating the app stream. Click Next.

Follow the instructions to download the config file for your app. Click Next.

Follow the instructions to add the Firebase SDK to your app. Click Next.

Run your app to verify installation of the SDK and that the app is communicating with Google servers. Click Finish.

Alternatively, click Skip this step if you want to complete app setup
  at a later time.  Click Next.

  Ref: https://support.google.com/analytics/answer/9304153

GTM also has two new tags in it - GA App+Web Configuration tag & GA App+Web Event.


问:
Best way for Processing to display graphics on a custom built remote display?
The goal is to use Processing as a scripting environment for creating graphics and to have the output display on a custom display device that is something like an LED light panel. The server running the program will be on a 1U rack. The idea is all the LED stuff is custom hardware, but rather than reinvent the wheel, it would be better to use an existing stack to drive the display. The problem is getting java to display on this device.

My initial thoughts are:
1. Run Java in headless mode.
2. Use Xvbf as the framebuffer.
3. Have a program run that reads the framebuffer, unpacks it, and then displays it on the remote device, at 30 fps.
4. Use Processing scripts to generate the graphics.

Does this make sense? Is there a better way? I'm not that knowledgeable about this area, but it seems better than trying to create a new java.awt.

答: I routinely use JFreeChart in headless mode with VNC to generate charts in servlets using ChartUtilities. It seems like you could just download the pictures to a picture frame via USB.


问:
Pushing element into an array wont update on the UI
I am trying to update an array after fetching the json data from the endpoint but i am not getting expected results on the front end.

```<code>export class LocationSectionComponent implements OnInit{

  myControl = new FormControl();
  options : string[] =  ['0089/PITT', '0085/CALI', '0090/MASS'];
  filteredOptions: Observable<string[]>;

  constructor(private http: HttpClient) {}

  ngOnInit() {
    this.filteredOptions = this.myControl.valueChanges
      .pipe(
        startWith(''),
        map(value => this._filter(value))
      );

      this.fetchData();
  }

  private _filter(value: string): string[] {
        const filterValue = value.toLowerCase();
        return this.options.filter(option => option.toLowerCase().includes(value));
      }

   private fetchData ()
   {
   this.http.get('http://localhost:9090/api/v1/facility/1')
   .subscribe(responseData => {
    const facilityOrigin = responseData.facilityNumber +"/"+ responseData.name;
    this.options.push(facilityOrigin);
    console.log(this.options);
    });
   }
</code>```

when I perform the push operation through fetchData() method gives me updated array like this

```<code>(4) ["0089/PITT", "0085/CALI", "0090/MASS", "89/PITT"]
0: "0089/PITT"
1: "0085/CALI"
2: "0090/MASS"
3: "89/PITT"
length: 4
__proto__: Array(0)
</code>```

but on the front end I can see only 3 options i.e ['0089/PITT', '0085/CALI', '0090/MASS']. How can fix this issue. Is this a scope issue or something else?

答:
Try

```<code>this.options = this.options.concat(facilityOrigin);
</code>```

instead of

```<code>this.options.push(facilityOrigin);
</code>```

Body:
I am trying to update an array after fetching the json data from the endpoint but i am not getting expected results on the front end.

export class LocationSectionComponent implements OnInit{

  myControl = new FormControl();
  options : string[] =  ['0089/PITT', '0085/CALI', '0090/MASS'];
  filteredOptions: Observable<string[]>;

  constructor(private http: HttpClient) {}

  ngOnInit() {
    this.filteredOptions = this.myControl.valueChanges
      .pipe(
        startWith(''),
        map(value => this._filter(value))
      );

      this.fetchData();
  }

  private _filter(value: string): string[] {
        const filterValue = value.toLowerCase();
        return this.options.filter(option => option.toLowerCase().includes(value));
      }

   private fetchData ()
   {
   this.http.get('http://localhost:9090/api/v1/facility/1')
   .subscribe(responseData => {
    const facilityOrigin = responseData.facilityNumber +"/"+ responseData.name;
    this.options.push(facilityOrigin);
    console.log(this.options);
    });
   }

when I perform the push operation through fetchData() method gives me updated array like this

(4) ["0089/PITT", "0085/CALI", "0090/MASS", "89/PITT"]
0: "0089/PITT"
1: "0085/CALI"
2: "0090/MASS"
3: "89/PITT"
length: 4
__proto__: Array(0)

but on the front end I can see only 3 options i.e ['0089/PITT', '0085/CALI', '0090/MASS']. How can fix this issue. Is this a scope issue or something else?

Body:
Try

this.options = this.options.concat(facilityOrigin);

instead of

this.options.push(facilityOrigin);


问:
Unable to decode base64 while read the data from console C#
I'm facing the issue while converting the encode/decode the data with C#. I have hard-coded the certain base64(encoded) data and its successfully decoded the string. As like below,

```<code>string encodedText = "eyJDb25uX0dyb3VwX0lEIjozMywiVXNlckVtYWlsIjoiVGVzdHNlcnZpc2VA\nZ21haWwuY29tIiwiVXNlclBhc3N3b3JkIjoib1ZkTEREWUVfX3FuSnZFSE1W\ncnR5WU5ZZzJSTnNzUnpaWG5KaFJMcCIsIkJhc2VVUkwiOiJodHRwOi8vbG9j\nYWxob3N0OjMwMDAifQ==\n";
byte[] data = Convert.FromBase64String(encodedText);
string decodedString = Encoding.UTF8.GetString(data);
</code>```

But, while reading the same value from the console, its failed to decode the data. For example,

```<code>string readLine = Console.ReadLine();
Console.WriteLine("Received Data :: " + readLine); // Exactly same data received here
byte[] encodedByte = Convert.FromBase64String(readLine); //Failed here?
string configData = System.Text.Encoding.UTF8.GetString(encodedByte);
</code>```

The second code failed with the below error message

```<code>Unhandled exception. System.FormatException: The input is not a valid Base-64 string as it contains a non-base 64 character, more than two padding characters, or an illegal character among the padding characters.
   at System.Convert.FromBase64CharPtr(Char* inputPtr, Int32 inputLength)
   at System.Convert.FromBase64String(String s)
</code>```

Note:

I have noticed that and removed all the \n from the given string. Now, its working fine. But, I'm not sure how to remove that \n programatically. I tried with the below codes, but it's does not working,

```<code>readLine = Regex.Replace(readLine, @"\t|\n|\r", String.Empty);
</code>```

And also tried with,

```<code>readLine = readLine.Replace("\n", String.Empty);
</code>```

It would be much appreciated if anyone help on this.

答:
The problem why it should not work, because when an string is input to the comand line '\n' would be escaped to '\\n'
Try this one:</p>

```<code>readLine = readLine.Replace("\\n", "");
//or
readLine = Regex.Replace(readLine, @"\\t|\\n|\\r", String.Empty);
</code>```

Body:
I'm facing the issue while converting the encode/decode the data with C#. I have hard-coded the certain base64(encoded) data and its successfully decoded the string. As like below,

string encodedText = "eyJDb25uX0dyb3VwX0lEIjozMywiVXNlckVtYWlsIjoiVGVzdHNlcnZpc2VA\nZ21haWwuY29tIiwiVXNlclBhc3N3b3JkIjoib1ZkTEREWUVfX3FuSnZFSE1W\ncnR5WU5ZZzJSTnNzUnpaWG5KaFJMcCIsIkJhc2VVUkwiOiJodHRwOi8vbG9j\nYWxob3N0OjMwMDAifQ==\n";
byte[] data = Convert.FromBase64String(encodedText);
string decodedString = Encoding.UTF8.GetString(data);

But, while reading the same value from the console, its failed to decode the data. For example,

string readLine = Console.ReadLine();
Console.WriteLine("Received Data :: " + readLine); // Exactly same data received here
byte[] encodedByte = Convert.FromBase64String(readLine); //Failed here?
string configData = System.Text.Encoding.UTF8.GetString(encodedByte);

The second code failed with the below error message

Unhandled exception. System.FormatException: The input is not a valid Base-64 string as it contains a non-base 64 character, more than two padding characters, or an illegal character among the padding characters.
   at System.Convert.FromBase64CharPtr(Char* inputPtr, Int32 inputLength)
   at System.Convert.FromBase64String(String s)

Note:

I have noticed that and removed all the \n from the given string. Now, its working fine. But, I'm not sure how to remove that \n programatically. I tried with the below codes, but it's does not working,

readLine = Regex.Replace(readLine, @"\t|\n|\r", String.Empty);

And also tried with,

readLine = readLine.Replace("\n", String.Empty);

It would be much appreciated if anyone help on this.

Body:
The problem why it should not work, because when an string is input to the comand line '\n' would be escaped to '\\n'
Try this one:

readLine = readLine.Replace("\\n", "");
//or
readLine = Regex.Replace(readLine, @"\\t|\\n|\\r", String.Empty);


问:
How to set d3 stroke width
At the moment the following draws two lines, but I can't seem to change the width:

```<code>var a = [23,50];
for (b = 0; b < a.length; b++) {
  var stripe = vis.selectAll("line.stripep")
    .data(connections.filter(function(d,i) { return i == a[b];  }))
    .enter().append("svg:line")
      .attr("class", "stripe")
      .attr("stroke", function(d) { return "#000000"; })
      .attr("stroke-linecap", 'round')
      .attr("stroke-width", 500)
      .attr("x1", function(d) { return x(d.station1.longitude); })
      .attr("y1", function(d) { return y(d.station1.latitude); })
      .attr("x2", function(d) { return x(d.station2.longitude); })
      .attr("y2", function(d) { return y(d.station2.latitude); })
}
</code>```

I want to set the width of the two black lines to 500.

At the moment I am trying:

```<code>.attr("stroke-width", 500)
</code>```

I have also tried:

```<code>.attr("stroke-width",  function(d) { return "500"; })
</code>```

This also did not work. What is the correct way to do this?

答: I just had the same problem. Solution: I found a .path entry within my CSS file with a 'stroke-width' setting.  CSS has priority :-)

Title: How to set d3 stroke width

Body:
At the moment the following draws two lines, but I can't seem to change the width:

var a = [23,50];
for (b = 0; b < a.length; b++) {
  var stripe = vis.selectAll("line.stripep")
    .data(connections.filter(function(d,i) { return i == a[b];  }))
    .enter().append("svg:line")
      .attr("class", "stripe")
      .attr("stroke", function(d) { return "#000000"; })
      .attr("stroke-linecap", 'round')
      .attr("stroke-width", 500)
      .attr("x1", function(d) { return x(d.station1.longitude); })
      .attr("y1", function(d) { return y(d.station1.latitude); })
      .attr("x2", function(d) { return x(d.station2.longitude); })
      .attr("y2", function(d) { return y(d.station2.latitude); })
}

I want to set the width of the two black lines to 500.

At the moment I am trying:

.attr("stroke-width", 500)

I have also tried:

.attr("stroke-width",  function(d) { return "500"; })

This also did not work. What is the correct way to do this?


问:
Ternary operator in perl yields unexpected result when assigning a variable in the statement
I'm trying to make a transition from php to perl and expectedly running into some weirdness.  I cannot understand why one version of my code works vs another.

This fails:

```<code>sub tester
{
$return;
($_[0] < 10) ? $return = "youre a youngin" : $return = "youre an old person";
return $return;
}

print "how old are you?";
$a = <>;
chomp $a;

print  tester($a);  #both result in "youre an old person"
</code>```

however this one works:

```<code>sub tester
{
    return ($_[0] < 10) ? "youre a youngin" : "youre an old person";
}

print "how old are you?";
$a = <>;
chomp $a;
print  tester($a);
</code>```

what is the real difference here?!

答:
It's a precedence thing.

```<code>($_[0] < 10) ? ($return = "youre a youngin") : ($return = "youre an old fart");
</code>```

...works better.

But honestly, no serious Perl programmer would write it like this.  First, they would put the following at the top of their program:

```<code>use warnings;
use strict;
</code>```

Second, they would put the argument into a local:

```<code>sub tester {
    my $arg = shift;
</code>```

Third, they would write the conditional in one of two ways:

```<code>if ($arg < 10) {
    $return = "you're a youngin";
}
else {
    $return = "you're an old fart";
}
</code>```

If they came from a Lisp background, they would probably dispense with the $return variable and just write:

```<code>return $arg < 10 ? "you're a youngin" : "you're an old fart";
</code>```

Body:
I'm trying to make a transition from php to perl and expectedly running into some weirdness.  I cannot understand why one version of my code works vs another.

This fails:

sub tester
{
$return;
($_[0] < 10) ? $return = "youre a youngin" : $return = "youre an old person";
return $return;
}

print "how old are you?";
$a = <>;
chomp $a;

print  tester($a);  #both result in "youre an old person"

however this one works:

sub tester
{
    return ($_[0] < 10) ? "youre a youngin" : "youre an old person";
}

print "how old are you?";
$a = <>;
chomp $a;
print  tester($a);

what is the real difference here?!

Body:
It's a precedence thing.

($_[0] < 10) ? ($return = "youre a youngin") : ($return = "youre an old fart");

...works better.

But honestly, no serious Perl programmer would write it like this.  First, they would put the following at the top of their program:

use warnings;
use strict;

Second, they would put the argument into a local:

sub tester {
    my $arg = shift;

Third, they would write the conditional in one of two ways:

if ($arg < 10) {
    $return = "you're a youngin";
}
else {
    $return = "you're an old fart";
}

If they came from a Lisp background, they would probably dispense with the $return variable and just write:

return $arg < 10 ? "you're a youngin" : "you're an old fart";


问:
Purge Command Discord.JS Works But Error Pops Up?
const Command = require('../../Structures/Command');
const Discord = require('discord.js');

```<code>module.exports = class extends Command {

    constructor(...args) {
        super(...args, {
            aliases: ['clear']
        });
    }

    async run(message, args) {
            // This command removes all messages from all users in the channel, up to 100.
            if(!message.member.hasPermission("KICK_MEMBERS"))
              return message.reply("Sorry, you don't have permissions to use this!");
            // get the delete count, as an actual number.
            const deleteCount = parseInt(args[0], 10);

            // Ooooh nice, combined conditions. <3
            if(!deleteCount || deleteCount < 1 || deleteCount > 100)
              return message.reply("Please provide a number between 1 and 100 for the number of messages to delete");

            // So we get our messages, and delete them. Simple enough, right?
            const fetched = await message.channel.messages.fetch({limit: deleteCount});
            message.channel.send(fetched.size + ` deleted messages by ${message.author.tag}`).then(msg => {msg.delete(2000)});
            message.channel.bulkDelete(fetched)
    }

};
</code>```

This works perfectly but there is an error saying:

```<code>(node:1132) UnhandledPromiseRejectionWarning: TypeError [INVALID_TYPE]: Supplied options is not an object.
    at Message.delete (C:\Users\xdswe\Desktop\Zero\node_modules\discord.js\src\structures\Message.js:501:44)
    at C:\Users\xdswe\Desktop\Zero\src\commands\Utilities\Purge.js:26:118
    at processTicksAndRejections (internal/process/task_queues.js:97:5)
(node:1132) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block,
or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=s
trict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 2)
</code>```

Please help

答:
That's caused by the last part of your code, since discord.js v12 Message.delete() needs an object with the options timeout and reason which are optional, but in your case you want timeout to be 2000 like so:

// So we get our messages, and delete them. Simple enough, right?
const fetched = await message.channel.messages.fetch({limit: deleteCount});
message.channel.send(fetched.size + ` deleted messages by ${message.author.tag}`)
  .then(msg => {msg.delete({ timeout: 2000 })});
message.channel.bulkDelete(fetched)
```

Body:
const Command = require('../../Structures/Command');
const Discord = require('discord.js');

module.exports = class extends Command {

    constructor(...args) {
        super(...args, {
            aliases: ['clear']
        });
    }

    async run(message, args) {
            // This command removes all messages from all users in the channel, up to 100.
            if(!message.member.hasPermission("KICK_MEMBERS"))
              return message.reply("Sorry, you don't have permissions to use this!");
            // get the delete count, as an actual number.
            const deleteCount = parseInt(args[0], 10);

            // Ooooh nice, combined conditions. <3
            if(!deleteCount || deleteCount < 1 || deleteCount > 100)
              return message.reply("Please provide a number between 1 and 100 for the number of messages to delete");

            // So we get our messages, and delete them. Simple enough, right?
            const fetched = await message.channel.messages.fetch({limit: deleteCount});
            message.channel.send(fetched.size + ` deleted messages by ${message.author.tag}`).then(msg => {msg.delete(2000)});
            message.channel.bulkDelete(fetched)
    }

};

This works perfectly but there is an error saying:

(node:1132) UnhandledPromiseRejectionWarning: TypeError [INVALID_TYPE]: Supplied options is not an object.
    at Message.delete (C:\Users\xdswe\Desktop\Zero\node_modules\discord.js\src\structures\Message.js:501:44)
    at C:\Users\xdswe\Desktop\Zero\src\commands\Utilities\Purge.js:26:118
    at processTicksAndRejections (internal/process/task_queues.js:97:5)
(node:1132) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block,
or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=s
trict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 2)

Please help


问:
Using the paginator directly is not recognising the configuration
CakePHP Version: 4.0.1

Introduction

I have 2 methods that both use the index view, index and search. On index the column can be selected from a select list and a value can be inputted via an input form control enabling a search by column and value. This data is sent via GET to the search method where empty values are checked and the query is executed and the index view is rendered.

In the later 3x versions with the below configuration the index view had the sort on the selected column which is what it is meant to do.

IE: Index view has due_date sorted on the initial load and I select task_name then submit the form to the search method. The task_name has the sort when the view is rendered.

TASKS CONTROLLER

Public pagination property:

```<code>public $paginate = [
    'sortWhitelist' => [
       'Tasks.due_date',
       'Tasks.task_name',
       'Tasks.type',
       'Tasks.priority',
       'Tasks.related_to_name',
       'Contacts.first_name',
       'Contacts.last_name',
       'Accounts.account_name',
       'Tasks.task_desc'
    ]
];
</code>```

Search Method

I initialise the data received from the index method and apply the config to the pagination property and send the query object to the view.

```<code>$this->setPage('');
$this->setSort($this->request->getQuery('column'));
$this->setDirection('asc');

// Validation of the page, sort, direction and limit is done here.
// IE: The $this->getSort() must be a string and not be numeric and has a strlen check
// and the $this->getDirection() can only be a string with values 'asc' or 'desc' etc.

if (!empty($this->getPage())) {
    $this->paginate['page'] = $this->getPage();
}
$this->paginate['sort'] = $this->getSort();
$this->paginate['direction'] = $this->getDirection();
$this->paginate['limit'] = $this->getLimit();

debug($this->paginate);

$tasks = $this->paginate($query);
$this->set(compact('tasks'));
</code>```

The result of debug is:

```<code>[
    'sortWhitelist' => [
        (int) 0 => 'Tasks.due_date',
        (int) 1 => 'Tasks.task_name',
        (int) 2 => 'Tasks.type',
        (int) 3 => 'Tasks.priority',
        (int) 4 => 'Tasks.related_to_name',
        (int) 5 => 'Contacts.first_name',
        (int) 6 => 'Contacts.last_name',
        (int) 7 => 'Accounts.account_name',
        (int) 8 => 'Tasks.task_desc'
    ],
    'sort' => 'Tasks.task_name',
    'direction' => 'asc',
    'limit' => (int) 25
 ]
</code>```

Result

The sort is on the task_name.

A couple of months ago I upgraded to 4 and have just revisted this functionality to find the sort is on the column that was present on index and not the column that was selected. I tried the below to fix the problem:

I referenced this information in the cookbook. And this from SO.

```<code>$config = $this->paginate = [
    'page' => $this->getPage(),
    'sort' => $this->getSort(),
    'direction' => $this->getDirection(),
    'limit' => $this->getLimit()
];

debug($config);

$tasks = $this->Paginator->paginate($query, $config);

debug($this->Paginator);

$this->set(compact('tasks'));
</code>```

The result of debug $config is:

```<code>[
    'page' => '',
    'sort' => 'Tasks.task_name',
    'direction' => 'asc',
    'limit' => (int) 25
]
</code>```

The result of debug $this->Paginator is:

```<code>object(Cake\Controller\Component\PaginatorComponent) {

    'components' => [],
    'implementedEvents' => [],
    '_config' => [
        'page' => (int) 1,
        'limit' => (int) 20,
        'maxLimit' => (int) 100,
        'whitelist' => [
            (int) 0 => 'limit',
            (int) 1 => 'sort',
            (int) 2 => 'page',
            (int) 3 => 'direction'
        ]
    ]

}
</code>```

NOTE: The whitelist contains limit, sort, page and direction? And the limit is 20 and I don't even have a selection of 20?

Result

The sort is on the due_date and I need it on the task_name.

Extra Info

If I then click the sort on task_name the sort is on the task_name. All the sorts work just not on the initial load?

Question

How can I configure the pagination property so the sort is on the task_name from the initial load of the search method.

Thanks Z.

答:
The fix is a bit costly and not ideal but it does work. I do a redirect on the initial load. Basically submit the form to search then redirect back to search. IE:

```<code>if ($this->request->getQuery('initial') === 'yes') {

    $redirect = $this->request->getQuery('redirect', [
        'action' => 'search',
         '?' => [
             'method' => 'search',
             'column' => $this->getColumn(),
             'input' => $this->getInput(),
             'page' => $this->getPage(),
             'sort' => $this->getSort(),
             'direction' => $this->getDirection(),
             'limit' => $this->getLimit(),
             'filter' => $this->getFilter(),
         ]
    ]);

    return $this->redirect($redirect);
    exit(0);

}

$config = $this->paginate = [
    'sortWhitelist' => [
       'Tasks.due_date',
       'Tasks.task_name',
       'Tasks.type',
       'Tasks.priority',
       'Tasks.related_to_name',
       'Contacts.first_name',
       'Contacts.last_name',
       'Accounts.account_name',
       'Tasks.task_desc'
     ],
     'page' => $this->getPage(),
     'sort' => $this->getSort(),
     'direction' => $this->getDirection(),
     'limit' => $this->getLimit()
 ];

 $tasks = $this->Paginator->paginate($query, $config);
 $this->set(compact('tasks'));
</code>```

The sort is now on the task_name.

This negates the initial load problem and simulates usage after the page initially loads where I know the sorts work.

Body:
CakePHP Version: 4.0.1

Introduction

I have 2 methods that both use the index view, index and search. On index the column can be selected from a select list and a value can be inputted via an input form control enabling a search by column and value. This data is sent via GET to the search method where empty values are checked and the query is executed and the index view is rendered.

In the later 3x versions with the below configuration the index view had the sort on the selected column which is what it is meant to do.

IE: Index view has due_date sorted on the initial load and I select task_name then submit the form to the search method. The task_name has the sort when the view is rendered.

TASKS CONTROLLER

Public pagination property:

public $paginate = [
    'sortWhitelist' => [
       'Tasks.due_date',
       'Tasks.task_name',
       'Tasks.type',
       'Tasks.priority',
       'Tasks.related_to_name',
       'Contacts.first_name',
       'Contacts.last_name',
       'Accounts.account_name',
       'Tasks.task_desc'
    ]
];

Search Method

I initialise the data received from the index method and apply the config to the pagination property and send the query object to the view.

$this->setPage('');
$this->setSort($this->request->getQuery('column'));
$this->setDirection('asc');

// Validation of the page, sort, direction and limit is done here.
// IE: The $this->getSort() must be a string and not be numeric and has a strlen check
// and the $this->getDirection() can only be a string with values 'asc' or 'desc' etc.

if (!empty($this->getPage())) {
    $this->paginate['page'] = $this->getPage();
}
$this->paginate['sort'] = $this->getSort();
$this->paginate['direction'] = $this->getDirection();
$this->paginate['limit'] = $this->getLimit();

debug($this->paginate);

$tasks = $this->paginate($query);
$this->set(compact('tasks'));

The result of debug is:

[
    'sortWhitelist' => [
        (int) 0 => 'Tasks.due_date',
        (int) 1 => 'Tasks.task_name',
        (int) 2 => 'Tasks.type',
        (int) 3 => 'Tasks.priority',
        (int) 4 => 'Tasks.related_to_name',
        (int) 5 => 'Contacts.first_name',
        (int) 6 => 'Contacts.last_name',
        (int) 7 => 'Accounts.account_name',
        (int) 8 => 'Tasks.task_desc'
    ],
    'sort' => 'Tasks.task_name',
    'direction' => 'asc',
    'limit' => (int) 25
 ]

Result

The sort is on the task_name.

A couple of months ago I upgraded to 4 and have just revisted this functionality to find the sort is on the column that was present on index and not the column that was selected. I tried the below to fix the problem:

I referenced this information in the cookbook. And this from SO.

$config = $this->paginate = [
    'page' => $this->getPage(),
    'sort' => $this->getSort(),
    'direction' => $this->getDirection(),
    'limit' => $this->getLimit()
];

debug($config);

$tasks = $this->Paginator->paginate($query, $config);

debug($this->Paginator);

$this->set(compact('tasks'));

The result of debug $config is:

[
    'page' => '',
    'sort' => 'Tasks.task_name',
    'direction' => 'asc',
    'limit' => (int) 25
]

The result of debug $this->Paginator is:

object(Cake\Controller\Component\PaginatorComponent) {

    'components' => [],
    'implementedEvents' => [],
    '_config' => [
        'page' => (int) 1,
        'limit' => (int) 20,
        'maxLimit' => (int) 100,
        'whitelist' => [
            (int) 0 => 'limit',
            (int) 1 => 'sort',
            (int) 2 => 'page',
            (int) 3 => 'direction'
        ]
    ]

}

NOTE: The whitelist contains limit, sort, page and direction? And the limit is 20 and I don't even have a selection of 20?

Result

The sort is on the due_date and I need it on the task_name.

Extra Info

If I then click the sort on task_name the sort is on the task_name. All the sorts work just not on the initial load?

Question

How can I configure the pagination property so the sort is on the task_name from the initial load of the search method.

Thanks Z.

Body:
The fix is a bit costly and not ideal but it does work. I do a redirect on the initial load. Basically submit the form to search then redirect back to search. IE:

if ($this->request->getQuery('initial') === 'yes') {

    $redirect = $this->request->getQuery('redirect', [
        'action' => 'search',
         '?' => [
             'method' => 'search',
             'column' => $this->getColumn(),
             'input' => $this->getInput(),
             'page' => $this->getPage(),
             'sort' => $this->getSort(),
             'direction' => $this->getDirection(),
             'limit' => $this->getLimit(),
             'filter' => $this->getFilter(),
         ]
    ]);

    return $this->redirect($redirect);
    exit(0);

}

$config = $this->paginate = [
    'sortWhitelist' => [
       'Tasks.due_date',
       'Tasks.task_name',
       'Tasks.type',
       'Tasks.priority',
       'Tasks.related_to_name',
       'Contacts.first_name',
       'Contacts.last_name',
       'Accounts.account_name',
       'Tasks.task_desc'
     ],
     'page' => $this->getPage(),
     'sort' => $this->getSort(),
     'direction' => $this->getDirection(),
     'limit' => $this->getLimit()
 ];

 $tasks = $this->Paginator->paginate($query, $config);
 $this->set(compact('tasks'));

The sort is now on the task_name.

This negates the initial load problem and simulates usage after the page initially loads where I know the sorts work.