BAEL-1917 spring security login page with react
This commit is contained in:
parent
7b68989ab1
commit
d075e24c7c
|
@ -17,6 +17,7 @@ addons:
|
|||
- oracle-java8-installer
|
||||
|
||||
cache:
|
||||
timeout: 900
|
||||
directories:
|
||||
- .autoconf
|
||||
- $HOME/.m2
|
||||
|
|
|
@ -0,0 +1,121 @@
|
|||
# Created by .ignore support plugin (hsz.mobi)
|
||||
### JetBrains template
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
|
||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||
|
||||
# User-specific stuff:
|
||||
.idea
|
||||
|
||||
# CMake
|
||||
cmake-build-debug/
|
||||
cmake-build-release/
|
||||
|
||||
## File-based project format:
|
||||
*.iws
|
||||
|
||||
## Plugin-specific files:
|
||||
|
||||
# IntelliJ
|
||||
out/
|
||||
|
||||
# mpeltonen/sbt-idea plugin
|
||||
.idea_modules/
|
||||
|
||||
# JIRA plugin
|
||||
atlassian-ide-plugin.xml
|
||||
|
||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||
com_crashlytics_export_strings.xml
|
||||
crashlytics.properties
|
||||
crashlytics-build.properties
|
||||
fabric.properties
|
||||
### Eclipse template
|
||||
|
||||
.metadata
|
||||
bin/
|
||||
tmp/
|
||||
*.tmp
|
||||
*.bak
|
||||
*.swp
|
||||
*~.nib
|
||||
local.properties
|
||||
.settings/
|
||||
.loadpath
|
||||
.recommenders
|
||||
|
||||
# External tool builders
|
||||
.externalToolBuilders/
|
||||
|
||||
# Locally stored "Eclipse launch configurations"
|
||||
*.launch
|
||||
|
||||
# PyDev specific (Python IDE for Eclipse)
|
||||
*.pydevproject
|
||||
|
||||
# CDT-specific (C/C++ Development Tooling)
|
||||
.cproject
|
||||
|
||||
# CDT- autotools
|
||||
.autotools
|
||||
|
||||
# Java annotation processor (APT)
|
||||
.factorypath
|
||||
|
||||
# PDT-specific (PHP Development Tools)
|
||||
.buildpath
|
||||
|
||||
# sbteclipse plugin
|
||||
.target
|
||||
|
||||
# Tern plugin
|
||||
.tern-project
|
||||
|
||||
# TeXlipse plugin
|
||||
.texlipse
|
||||
|
||||
# STS (Spring Tool Suite)
|
||||
.springBeans
|
||||
|
||||
# Code Recommenders
|
||||
.recommenders/
|
||||
|
||||
# Scala IDE specific (Scala & Java development for Eclipse)
|
||||
.cache-main
|
||||
.scala_dependencies
|
||||
.worksheet
|
||||
### Maven template
|
||||
target/
|
||||
pom.xml.tag
|
||||
pom.xml.releaseBackup
|
||||
pom.xml.versionsBackup
|
||||
pom.xml.next
|
||||
release.properties
|
||||
dependency-reduced-pom.xml
|
||||
buildNumber.properties
|
||||
.mvn/timing.properties
|
||||
|
||||
# Avoid ignoring Maven wrapper jar file (.jar files are usually ignored)
|
||||
!/.mvn/wrapper/maven-wrapper.jar
|
||||
### Java template
|
||||
# Compiled class file
|
||||
*.class
|
||||
|
||||
# Log file
|
||||
*.log
|
||||
|
||||
# BlueJ files
|
||||
*.ctxt
|
||||
|
||||
# Mobile Tools for Java (J2ME)
|
||||
.mtj.tmp/
|
||||
|
||||
# Package Files #
|
||||
*.jar
|
||||
*.war
|
||||
*.ear
|
||||
*.zip
|
||||
*.tar.gz
|
||||
*.rar
|
||||
|
||||
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||
hs_err_pid*
|
|
@ -0,0 +1,15 @@
|
|||
=========
|
||||
|
||||
## Spring Security React Example Project
|
||||
|
||||
###The Course
|
||||
|
||||
The "Learn Spring Security" Classes: http://github.learnspringsecurity.com
|
||||
|
||||
### Relevant Articles:
|
||||
|
||||
### Build the Project
|
||||
|
||||
```
|
||||
mvn clean install
|
||||
```
|
|
@ -0,0 +1,183 @@
|
|||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>spring-security-react</artifactId>
|
||||
<version>0.1-SNAPSHOT</version>
|
||||
|
||||
<name>spring-security-react</name>
|
||||
<packaging>war</packaging>
|
||||
|
||||
<parent>
|
||||
<groupId>com.baeldung</groupId>
|
||||
<artifactId>parent-spring-4</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<relativePath>../parent-spring-4</relativePath>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!-- Spring Security -->
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-web</artifactId>
|
||||
<version>${org.springframework.security.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-config</artifactId>
|
||||
<version>${org.springframework.security.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-taglibs</artifactId>
|
||||
<version>${org.springframework.security.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Spring -->
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-core</artifactId>
|
||||
<version>${org.springframework.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-context</artifactId>
|
||||
<version>${org.springframework.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-beans</artifactId>
|
||||
<version>${org.springframework.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-aop</artifactId>
|
||||
<version>${org.springframework.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-web</artifactId>
|
||||
<version>${org.springframework.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-webmvc</artifactId>
|
||||
<version>${org.springframework.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- web -->
|
||||
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
<version>${javax.servlet.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>jstl</artifactId>
|
||||
<version>${jstl.version}</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<finalName>spring-security-react</finalName>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<filtering>true</filtering>
|
||||
</resource>
|
||||
</resources>
|
||||
|
||||
<plugins>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-war-plugin</artifactId>
|
||||
<version>${maven-war-plugin.version}</version>
|
||||
<configuration>
|
||||
<warSourceExcludes>
|
||||
WEB-INF/view/react/node/**,
|
||||
WEB-INF/view/react/node_modules/**,
|
||||
WEB-INF/view/react/public/**,
|
||||
WEB-INF/view/react/src/**,
|
||||
WEB-INF/view/react/*
|
||||
</warSourceExcludes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>com.github.eirslett</groupId>
|
||||
<artifactId>frontend-maven-plugin</artifactId>
|
||||
<version>${frontend-maven-plugin.version}</version>
|
||||
<configuration>
|
||||
<nodeVersion>${node.version}</nodeVersion>
|
||||
<npmVersion>${npm.version}</npmVersion>
|
||||
<workingDirectory>src/main/webapp/WEB-INF/view/react</workingDirectory>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>install node and npm</id>
|
||||
<goals>
|
||||
<goal>install-node-and-npm</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>npm install</id>
|
||||
<goals>
|
||||
<goal>npm</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>npm run build</id>
|
||||
<goals>
|
||||
<goal>npm</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<arguments>run build</arguments>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-maven-plugin</artifactId>
|
||||
<version>9.4.11.v20180605</version>
|
||||
</plugin>
|
||||
|
||||
</plugins>
|
||||
|
||||
</build>
|
||||
|
||||
<properties>
|
||||
<!-- Spring -->
|
||||
<org.springframework.version>4.3.6.RELEASE</org.springframework.version>
|
||||
<org.springframework.security.version>4.2.1.RELEASE</org.springframework.security.version>
|
||||
|
||||
<!-- various -->
|
||||
<javax.servlet.version>3.1.0</javax.servlet.version>
|
||||
<jstl.version>1.2</jstl.version>
|
||||
|
||||
<!-- util -->
|
||||
<guava.version>19.0</guava.version>
|
||||
<commons-lang3.version>3.5</commons-lang3.version>
|
||||
|
||||
<!-- Maven plugins -->
|
||||
<maven-war-plugin.version>2.6</maven-war-plugin.version>
|
||||
<maven-resources-plugin.version>2.7</maven-resources-plugin.version>
|
||||
<frontend-maven-plugin.version>1.6</frontend-maven-plugin.version>
|
||||
<jetty.version>9.4.11.v20180605</jetty.version>
|
||||
|
||||
<!-- frontend -->
|
||||
<node.version>v8.11.3</node.version>
|
||||
<npm.version>6.1.0</npm.version>
|
||||
|
||||
</properties>
|
||||
|
||||
</project>
|
|
@ -0,0 +1,55 @@
|
|||
package org.baeldung.spring;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.ViewResolver;
|
||||
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
||||
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
|
||||
import org.springframework.web.servlet.view.InternalResourceViewResolver;
|
||||
import org.springframework.web.servlet.view.JstlView;
|
||||
|
||||
@EnableWebMvc
|
||||
@Configuration
|
||||
public class MvcConfig extends WebMvcConfigurerAdapter {
|
||||
|
||||
public MvcConfig() {
|
||||
super();
|
||||
}
|
||||
|
||||
// API
|
||||
|
||||
@Override
|
||||
public void addViewControllers(final ViewControllerRegistry registry) {
|
||||
super.addViewControllers(registry);
|
||||
|
||||
registry.addViewController("/anonymous.html");
|
||||
|
||||
registry.addViewController("/login.html");
|
||||
registry.addViewController("/homepage.html");
|
||||
registry.addViewController("/admin/adminpage.html");
|
||||
registry.addViewController("/accessDenied");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addResourceHandlers(ResourceHandlerRegistry registry) {
|
||||
registry.addResourceHandler("/static/**").addResourceLocations("/WEB-INF/view/react/build/static/");
|
||||
|
||||
registry.addResourceHandler("/*.js").addResourceLocations("/WEB-INF/view/react/build/");
|
||||
registry.addResourceHandler("/*.json").addResourceLocations("/WEB-INF/view/react/build/");
|
||||
registry.addResourceHandler("/*.ico").addResourceLocations("/WEB-INF/view/react/build/");
|
||||
registry.addResourceHandler("/index.html").addResourceLocations("/WEB-INF/view/react/build/index.html");
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ViewResolver viewResolver() {
|
||||
final InternalResourceViewResolver bean = new InternalResourceViewResolver();
|
||||
|
||||
bean.setViewClass(JstlView.class);
|
||||
bean.setPrefix("/WEB-INF/view/");
|
||||
bean.setSuffix(".jsp");
|
||||
|
||||
return bean;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
package org.baeldung.spring;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Profile;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
@Profile("!https")
|
||||
public class SecSecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
|
||||
public SecSecurityConfig() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
|
||||
// @formatter:off
|
||||
auth.inMemoryAuthentication()
|
||||
.withUser("user1").password("user1Pass").roles("USER")
|
||||
.and()
|
||||
.withUser("user2").password("user2Pass").roles("USER")
|
||||
.and()
|
||||
.withUser("admin").password("admin0Pass").roles("ADMIN");
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(final HttpSecurity http) throws Exception {
|
||||
// @formatter:off
|
||||
http
|
||||
.csrf().disable()
|
||||
.authorizeRequests()
|
||||
.antMatchers("/admin/**").hasRole("ADMIN")
|
||||
.antMatchers("/anonymous*").anonymous()
|
||||
.antMatchers(HttpMethod.GET, "/index*", "/static/**", "/*.js", "/*.json", "/*.ico").permitAll()
|
||||
.anyRequest().authenticated()
|
||||
.and()
|
||||
.formLogin()
|
||||
.loginPage("/index.html")
|
||||
.loginProcessingUrl("/perform_login")
|
||||
.defaultSuccessUrl("/homepage.html",true)
|
||||
.failureUrl("/index.html?error=true")
|
||||
.and()
|
||||
.logout()
|
||||
.logoutUrl("/perform_logout")
|
||||
.deleteCookies("JSESSIONID");
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration>
|
||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>web - %date [%thread] %-5level %logger{36} - %message%n
|
||||
</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<logger name="org.springframework" level="WARN" />
|
||||
<logger name="org.springframework.transaction" level="WARN" />
|
||||
|
||||
<!-- in order to debug some marshalling issues, this needs to be TRACE -->
|
||||
<logger name="org.springframework.web.servlet.mvc" level="INFO" />
|
||||
|
||||
<root level="INFO">
|
||||
<appender-ref ref="STDOUT" />
|
||||
</root>
|
||||
</configuration>
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd" >
|
||||
|
||||
</beans>
|
|
@ -0,0 +1,15 @@
|
|||
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
|
||||
pageEncoding="ISO-8859-1"%>
|
||||
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
|
||||
<title>Access Denied</title>
|
||||
</head>
|
||||
<body>
|
||||
<h2>Sorry, you do not have permission to view this page.</h2>
|
||||
|
||||
Click <a href="<c:url value="/homepage.html" /> ">here</a> to go back to the Homepage.
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,15 @@
|
|||
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
|
||||
<%@ taglib prefix="security" uri="http://www.springframework.org/security/tags" %>
|
||||
<html>
|
||||
<head></head>
|
||||
|
||||
<body>
|
||||
<h1>This is the body of the sample admin page</h1>
|
||||
|
||||
This page is only visible to an admin
|
||||
<br/>
|
||||
|
||||
<a href="<c:url value="/perform_logout" />">Logout</a>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,10 @@
|
|||
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
|
||||
<html>
|
||||
<head></head>
|
||||
|
||||
<body>
|
||||
<h1>Anonymous page</h1>
|
||||
|
||||
<a href="<c:url value="/login.html" />">To Login</a>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,26 @@
|
|||
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
|
||||
<%@ taglib prefix="security" uri="http://www.springframework.org/security/tags" %>
|
||||
<html>
|
||||
<head></head>
|
||||
|
||||
<body>
|
||||
<h1>This is the body of the sample view</h1>
|
||||
|
||||
<security:authorize access="hasRole('ROLE_USER')">
|
||||
This text is only visible to a user
|
||||
<br/> <br/>
|
||||
<a href="<c:url value="/admin/adminpage.html" />">Restricted Admin Page</a>
|
||||
<br/> <br/>
|
||||
</security:authorize>
|
||||
|
||||
<security:authorize access="hasRole('ROLE_ADMIN')">
|
||||
This text is only visible to an admin
|
||||
<br/>
|
||||
<a href="<c:url value="/admin/adminpage.html" />">Admin Page</a>
|
||||
<br/>
|
||||
</security:authorize>
|
||||
|
||||
<a href="<c:url value="/perform_logout" />">Logout</a>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"presets": ["env"]
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
build
|
||||
node_modules
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"rules": {
|
||||
"linebreak-style": [2, "unix"],
|
||||
"semi":"off",
|
||||
"no-console": "off"
|
||||
},
|
||||
"ecmaFeatures": {
|
||||
"jsx": true
|
||||
},
|
||||
"env": {
|
||||
"es6": true,
|
||||
"browser": true,
|
||||
"node": true,
|
||||
"mocha": true
|
||||
},
|
||||
"plugins": ["react"],
|
||||
"parser": "babel-eslint",
|
||||
"parserOptions": {
|
||||
"sourceType": "module"
|
||||
},
|
||||
"extends": ["eslint:recommended", "plugin:react/recommended"]
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
# See https://help.github.com/ignore-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
node_modules
|
||||
node
|
||||
|
||||
# development
|
||||
build
|
||||
|
||||
# testing
|
||||
coverage
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
11295
spring-security-react/src/main/webapp/WEB-INF/view/react/package-lock.json
generated
Normal file
11295
spring-security-react/src/main/webapp/WEB-INF/view/react/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"name": "react",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"prop-types": "^15.6.2",
|
||||
"react": "^16.4.1",
|
||||
"react-dom": "^16.4.1",
|
||||
"react-scripts": "1.1.4"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
"build": "react-scripts build",
|
||||
"test": "react-scripts test --env=jsdom",
|
||||
"eject": "react-scripts eject"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint-plugin-react": "^7.9.1"
|
||||
}
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 3.8 KiB |
|
@ -0,0 +1,30 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<meta name="theme-color" content="#000000">
|
||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
|
||||
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
|
||||
<!--
|
||||
Notice the use of %PUBLIC_URL% in the tags above.
|
||||
It will be replaced with the URL of the `public` folder during the build.
|
||||
Only files inside the `public` folder can be referenced from the HTML.
|
||||
|
||||
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
||||
work correctly both with client-side routing and a non-root public URL.
|
||||
Learn how to configure a non-root public URL by running `npm run build`.
|
||||
-->
|
||||
<title>Spring Login - React App</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>
|
||||
You need to enable JavaScript to run this app.
|
||||
</noscript>
|
||||
|
||||
<div id="root">
|
||||
<div id="container"></div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"short_name": "React App",
|
||||
"name": "Create React App Sample",
|
||||
"icons": [
|
||||
{
|
||||
"src": "favicon.ico",
|
||||
"sizes": "64x64 32x32 24x24 16x16",
|
||||
"type": "image/x-icon"
|
||||
}
|
||||
],
|
||||
"start_url": "./index.html",
|
||||
"display": "standalone",
|
||||
"theme_color": "#000000",
|
||||
"background_color": "#ffffff"
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
import React, { Component } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import Input from './Input'
|
||||
|
||||
class Form extends Component {
|
||||
|
||||
constructor(props){
|
||||
super(props)
|
||||
if(props.error){
|
||||
this.state = { failure: 'wrong username or password!', errcount: 0 }
|
||||
}else{
|
||||
this.state = { errcount: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
handleSubmit = (event) => {
|
||||
event.preventDefault()
|
||||
if(!this.state.errcount) {
|
||||
const data = new FormData(this.form)
|
||||
fetch(this.form.action, {
|
||||
method: this.form.method,
|
||||
body: new URLSearchParams(data)
|
||||
}).then(v => {
|
||||
if(v.redirected) window.location = v.url
|
||||
})
|
||||
.catch(e => console.warn(e))
|
||||
}
|
||||
}
|
||||
|
||||
handleError = (field, errmsg) => {
|
||||
if(!field) return
|
||||
|
||||
if(errmsg){
|
||||
this.setState((prevState) => ({
|
||||
failure: '',
|
||||
errcount: prevState.errcount + 1,
|
||||
errmsgs: {...prevState.errmsgs, [field]: errmsg}
|
||||
}))
|
||||
} else {
|
||||
this.setState((prevState) => ({
|
||||
failure: '',
|
||||
errcount: prevState.errcount===1? 0 : prevState.errcount-1,
|
||||
errmsgs: {...prevState.errmsgs, [field]: ''}
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
renderError = () => {
|
||||
if(this.state.errcount || this.state.failure) {
|
||||
const errmsg = this.state.failure || Object.values(this.state.errmsgs).find(v=>v)
|
||||
console.log(`error: ${errmsg}`)
|
||||
return <div className="error">{errmsg}</div>
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const inputs = this.props.inputs.map(
|
||||
({name, placeholder, type, value, className}, index) => (
|
||||
<Input key={index} name={name} placeholder={placeholder} type={type} value={value}
|
||||
className={type==='submit'? className : ''} handleError={this.handleError} />
|
||||
)
|
||||
)
|
||||
const errors = this.renderError()
|
||||
return (
|
||||
<form {...this.props} onSubmit={this.handleSubmit} ref={fm => {this.form=fm}} >
|
||||
{inputs}
|
||||
{errors}
|
||||
</form>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Form.propTypes = {
|
||||
name: PropTypes.string,
|
||||
action: PropTypes.string,
|
||||
method: PropTypes.string,
|
||||
inputs: PropTypes.array,
|
||||
error: PropTypes.string
|
||||
}
|
||||
|
||||
export default Form
|
|
@ -0,0 +1,57 @@
|
|||
import React, { Component } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
class Input extends Component {
|
||||
|
||||
constructor(props){
|
||||
super(props)
|
||||
this.state = {
|
||||
value: props.value? props.value : '',
|
||||
className: props.className? props.className : '',
|
||||
error: false
|
||||
}
|
||||
}
|
||||
|
||||
inputChange = (event) => {
|
||||
const value = event.target.value, name = event.target.name
|
||||
//switch(name) {
|
||||
// case 'username': this.validate(name, value, /^([a-zA-Z0-9.]{4,})$/, 'invalid username')
|
||||
// break;
|
||||
// case 'password': this.validate(name, value, /^(?=.*[A-Z])(?=.*[0-9])(?=.*[a-z]).{6,}$/, 'insecure password')
|
||||
// break;
|
||||
// default:
|
||||
// console.warn(`unknown field ${name}`)
|
||||
//}
|
||||
this.setState({ value: value })
|
||||
}
|
||||
|
||||
validate = (name, value, validRegex, warnmsg) => {
|
||||
const invalid = !value || !validRegex.test(value)
|
||||
if(!this.state.error && invalid) {
|
||||
this.setState({ className: 'input-error', error: true })
|
||||
this.handleError(name, warnmsg)
|
||||
}else if(this.state.error && !invalid) {
|
||||
this.setState({ className: '', error: false })
|
||||
this.handleError(name)
|
||||
}
|
||||
}
|
||||
|
||||
render (){
|
||||
const {handleError, ...opts} = this.props
|
||||
this.handleError = handleError
|
||||
return (
|
||||
<input {...opts} value={this.state.value} onChange={this.inputChange} className={this.state.className} />
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Input.propTypes = {
|
||||
name: PropTypes.string,
|
||||
placeholder: PropTypes.string,
|
||||
type: PropTypes.string,
|
||||
className: PropTypes.string,
|
||||
handleError: PropTypes.func,
|
||||
value: PropTypes.string
|
||||
}
|
||||
|
||||
export default Input
|
|
@ -0,0 +1,61 @@
|
|||
body {
|
||||
font-family: "Open Sans", "Roboto", arial, sans-serif;
|
||||
background: #e9e9e9;
|
||||
}
|
||||
|
||||
#root {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 0;
|
||||
left: 0;
|
||||
margin-right: auto;
|
||||
margin-left: auto;
|
||||
transform: translateY(-50%);
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
height: 110%;
|
||||
border-radius: .19rem;
|
||||
box-shadow: 0px .19rem .41rem rgba(0,0,0,.25);
|
||||
}
|
||||
|
||||
form {
|
||||
height: 100%;
|
||||
padding: 1.5rem;
|
||||
background: #fafafa;
|
||||
}
|
||||
|
||||
input {
|
||||
background-color: transparent;
|
||||
border-top: 0;
|
||||
border-right: 0;
|
||||
border-left: 0;
|
||||
border-radius: 0;
|
||||
color: #111;
|
||||
font-size: 1rem;
|
||||
padding: .3rem .5rem;
|
||||
margin-bottom: 1rem;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.input-error {
|
||||
border-color: #FF0000;
|
||||
}
|
||||
|
||||
.error {
|
||||
color: #FF0000;
|
||||
padding: .2rem;
|
||||
}
|
||||
|
||||
.btn {
|
||||
padding: .5rem 1.5rem;
|
||||
font-size: .85rem;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
color: #fff;
|
||||
background: #111;
|
||||
border-radius: .25rem;
|
||||
margin: 0;
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
import React from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import './index.css'
|
||||
import Form from './Form'
|
||||
|
||||
const inputs = [{
|
||||
name: "username",
|
||||
placeholder: "username",
|
||||
type: "text"
|
||||
},{
|
||||
name: "password",
|
||||
placeholder: "password",
|
||||
type: "password"
|
||||
},{
|
||||
type: "submit",
|
||||
value: "Submit",
|
||||
className: "btn"
|
||||
}]
|
||||
|
||||
const props = {name: 'loginForm', method: 'POST', action: '/perform_login', inputs: inputs}
|
||||
|
||||
const params = new URLSearchParams(window.location.search)
|
||||
|
||||
ReactDOM.render(<Form {...props} error={params.get('error')} />, document.getElementById('container'))
|
||||
|
|
@ -0,0 +1,117 @@
|
|||
// In production, we register a service worker to serve assets from local cache.
|
||||
|
||||
// This lets the app load faster on subsequent visits in production, and gives
|
||||
// it offline capabilities. However, it also means that developers (and users)
|
||||
// will only see deployed updates on the "N+1" visit to a page, since previously
|
||||
// cached resources are updated in the background.
|
||||
|
||||
// To learn more about the benefits of this model, read https://goo.gl/KwvDNy.
|
||||
// This link also includes instructions on opting out of this behavior.
|
||||
|
||||
const isLocalhost = Boolean(
|
||||
window.location.hostname === 'localhost' ||
|
||||
// [::1] is the IPv6 localhost address.
|
||||
window.location.hostname === '[::1]' ||
|
||||
// 127.0.0.1/8 is considered localhost for IPv4.
|
||||
window.location.hostname.match(
|
||||
/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
|
||||
)
|
||||
);
|
||||
|
||||
export default function register() {
|
||||
if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
|
||||
// The URL constructor is available in all browsers that support SW.
|
||||
const publicUrl = new URL(process.env.PUBLIC_URL, window.location);
|
||||
if (publicUrl.origin !== window.location.origin) {
|
||||
// Our service worker won't work if PUBLIC_URL is on a different origin
|
||||
// from what our page is served on. This might happen if a CDN is used to
|
||||
// serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374
|
||||
return;
|
||||
}
|
||||
|
||||
window.addEventListener('load', () => {
|
||||
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
|
||||
|
||||
if (isLocalhost) {
|
||||
// This is running on localhost. Lets check if a service worker still exists or not.
|
||||
checkValidServiceWorker(swUrl);
|
||||
|
||||
// Add some additional logging to localhost, pointing developers to the
|
||||
// service worker/PWA documentation.
|
||||
navigator.serviceWorker.ready.then(() => {
|
||||
console.log(
|
||||
'This web app is being served cache-first by a service ' +
|
||||
'worker. To learn more, visit https://goo.gl/SC7cgQ'
|
||||
);
|
||||
});
|
||||
} else {
|
||||
// Is not local host. Just register service worker
|
||||
registerValidSW(swUrl);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function registerValidSW(swUrl) {
|
||||
navigator.serviceWorker
|
||||
.register(swUrl)
|
||||
.then(registration => {
|
||||
registration.onupdatefound = () => {
|
||||
const installingWorker = registration.installing;
|
||||
installingWorker.onstatechange = () => {
|
||||
if (installingWorker.state === 'installed') {
|
||||
if (navigator.serviceWorker.controller) {
|
||||
// At this point, the old content will have been purged and
|
||||
// the fresh content will have been added to the cache.
|
||||
// It's the perfect time to display a "New content is
|
||||
// available; please refresh." message in your web app.
|
||||
console.log('New content is available; please refresh.');
|
||||
} else {
|
||||
// At this point, everything has been precached.
|
||||
// It's the perfect time to display a
|
||||
// "Content is cached for offline use." message.
|
||||
console.log('Content is cached for offline use.');
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error during service worker registration:', error);
|
||||
});
|
||||
}
|
||||
|
||||
function checkValidServiceWorker(swUrl) {
|
||||
// Check if the service worker can be found. If it can't reload the page.
|
||||
fetch(swUrl)
|
||||
.then(response => {
|
||||
// Ensure service worker exists, and that we really are getting a JS file.
|
||||
if (
|
||||
response.status === 404 ||
|
||||
response.headers.get('content-type').indexOf('javascript') === -1
|
||||
) {
|
||||
// No service worker found. Probably a different app. Reload the page.
|
||||
navigator.serviceWorker.ready.then(registration => {
|
||||
registration.unregister().then(() => {
|
||||
window.location.reload();
|
||||
});
|
||||
});
|
||||
} else {
|
||||
// Service worker found. Proceed as normal.
|
||||
registerValidSW(swUrl);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
console.log(
|
||||
'No internet connection found. App is running in offline mode.'
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
export function unregister() {
|
||||
if ('serviceWorker' in navigator) {
|
||||
navigator.serviceWorker.ready.then(registration => {
|
||||
registration.unregister();
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
|
||||
version="3.1">
|
||||
|
||||
<display-name>Spring MVC Application</display-name>
|
||||
|
||||
<!-- Spring root -->
|
||||
<context-param>
|
||||
<param-name>contextClass</param-name>
|
||||
<param-value>
|
||||
org.springframework.web.context.support.AnnotationConfigWebApplicationContext
|
||||
</param-value>
|
||||
</context-param>
|
||||
<context-param>
|
||||
<param-name>contextConfigLocation</param-name>
|
||||
<param-value>org.baeldung.spring</param-value>
|
||||
</context-param>
|
||||
|
||||
<listener>
|
||||
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
|
||||
</listener>
|
||||
|
||||
<!-- Spring child -->
|
||||
<servlet>
|
||||
<servlet-name>mvc</servlet-name>
|
||||
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
|
||||
<load-on-startup>1</load-on-startup>
|
||||
</servlet>
|
||||
<servlet-mapping>
|
||||
<servlet-name>mvc</servlet-name>
|
||||
<url-pattern>/</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<!-- Spring Security -->
|
||||
<filter>
|
||||
<filter-name>springSecurityFilterChain</filter-name>
|
||||
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
|
||||
</filter>
|
||||
<filter-mapping>
|
||||
<filter-name>springSecurityFilterChain</filter-name>
|
||||
<url-pattern>/*</url-pattern>
|
||||
</filter-mapping>
|
||||
|
||||
<error-page>
|
||||
<error-code>403</error-code>
|
||||
<location>/accessDenied</location>
|
||||
</error-page>
|
||||
|
||||
<!-- <welcome-file-list> -->
|
||||
<!-- <welcome-file>index.html</welcome-file> -->
|
||||
<!-- </welcome-file-list> -->
|
||||
|
||||
|
||||
|
||||
</web-app>
|
Loading…
Reference in New Issue