├── spring-security-phone-samples ├── spring-security-phone-samples-helloworld │ ├── src │ │ └── main │ │ │ ├── resources │ │ │ ├── application.properties │ │ │ ├── messages.properties │ │ │ ├── messages_zh_CN.properties │ │ │ ├── log4j2.xml │ │ │ └── templates │ │ │ │ ├── layout.html │ │ │ │ ├── login.html │ │ │ │ └── login │ │ │ │ └── phone.html │ │ │ └── java │ │ │ └── org │ │ │ └── oxerr │ │ │ └── spring │ │ │ └── security │ │ │ └── phone │ │ │ └── samples │ │ │ └── helloworld │ │ │ ├── package-info.java │ │ │ ├── web │ │ │ ├── package-info.java │ │ │ └── IndexController.java │ │ │ ├── SecurityConfiguration.java │ │ │ ├── WebMvcConfiguration.java │ │ │ └── Application.java │ └── pom.xml └── pom.xml ├── spring-security-phone-web ├── src │ └── main │ │ └── java │ │ └── org │ │ └── oxerr │ │ └── spring │ │ └── security │ │ └── phone │ │ └── web │ │ └── authentication │ │ ├── package-info.java │ │ └── PhoneAuthenticationFilter.java └── pom.xml ├── spring-security-phone-config ├── src │ └── main │ │ └── java │ │ └── org │ │ └── oxerr │ │ └── spring │ │ └── security │ │ └── phone │ │ └── config │ │ └── annotation │ │ └── web │ │ └── configurers │ │ ├── package-info.java │ │ └── PhoneLoginConfigurer.java └── pom.xml ├── spring-security-phone-core ├── src │ └── main │ │ └── java │ │ └── org │ │ └── oxerr │ │ └── spring │ │ └── security │ │ └── phone │ │ ├── core │ │ ├── package-info.java │ │ └── userdetails │ │ │ ├── PhoneUserDetailsService.java │ │ │ └── PhoneNumberNotFoundException.java │ │ └── authentication │ │ ├── PhoneAuthenticationToken.java │ │ └── PhoneAuthenticationProvider.java └── pom.xml ├── maven-version-rules.xml ├── src └── site │ └── site.xml ├── .gitignore ├── spring-security-phone-coverage └── pom.xml └── pom.xml /spring-security-phone-samples/spring-security-phone-samples-helloworld/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.thymeleaf.cache=false 2 | -------------------------------------------------------------------------------- /spring-security-phone-web/src/main/java/org/oxerr/spring/security/phone/web/authentication/package-info.java: -------------------------------------------------------------------------------- 1 | package org.oxerr.spring.security.phone.web.authentication; 2 | -------------------------------------------------------------------------------- /spring-security-phone-config/src/main/java/org/oxerr/spring/security/phone/config/annotation/web/configurers/package-info.java: -------------------------------------------------------------------------------- 1 | package org.oxerr.spring.security.phone.config.annotation.web.configurers; 2 | -------------------------------------------------------------------------------- /spring-security-phone-samples/spring-security-phone-samples-helloworld/src/main/java/org/oxerr/spring/security/phone/samples/helloworld/package-info.java: -------------------------------------------------------------------------------- 1 | package org.oxerr.spring.security.phone.samples.helloworld; 2 | -------------------------------------------------------------------------------- /spring-security-phone-samples/spring-security-phone-samples-helloworld/src/main/java/org/oxerr/spring/security/phone/samples/helloworld/web/package-info.java: -------------------------------------------------------------------------------- 1 | package org.oxerr.spring.security.phone.samples.helloworld.web; 2 | -------------------------------------------------------------------------------- /spring-security-phone-core/src/main/java/org/oxerr/spring/security/phone/core/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Authenticates users via phone number & verification code. 3 | */ 4 | package org.oxerr.spring.security.phone.core; 5 | -------------------------------------------------------------------------------- /spring-security-phone-core/src/main/java/org/oxerr/spring/security/phone/core/userdetails/PhoneUserDetailsService.java: -------------------------------------------------------------------------------- 1 | package org.oxerr.spring.security.phone.core.userdetails; 2 | 3 | import org.springframework.security.core.userdetails.UserDetails; 4 | 5 | public interface PhoneUserDetailsService { 6 | 7 | boolean consumeCode(String number, String code); 8 | 9 | UserDetails loadUserByNumber(String number) throws PhoneNumberNotFoundException; 10 | 11 | } 12 | -------------------------------------------------------------------------------- /spring-security-phone-samples/spring-security-phone-samples-helloworld/src/main/resources/messages.properties: -------------------------------------------------------------------------------- 1 | login.title=Please Sign In 2 | login.failure=Your login attempt was not successful, try again. 3 | login.failure.reason=Reason: 4 | login.rememberMe=Remember me on this computer. 5 | login.username=Username 6 | login.password=Password 7 | login.login=Login 8 | 9 | login.type.password=Login via Username 10 | login.type.phone=Login via Phone 11 | 12 | phone.number=Phone number 13 | phone.code=Code 14 | phone.code.send=Send code 15 | -------------------------------------------------------------------------------- /maven-version-rules.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 10 | .*\-b\d+ 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /spring-security-phone-samples/spring-security-phone-samples-helloworld/src/main/resources/messages_zh_CN.properties: -------------------------------------------------------------------------------- 1 | login.title=\u8bf7\u767b\u5f55 2 | login.failure=\u767b\u5f55\u5931\u8d25\uff0c\u8bf7\u91cd\u8bd5\u3002 3 | login.failure.reason=\u539f\u56e0\uff1a 4 | login.rememberMe=\u5728\u8fd9\u53f0\u8bbe\u5907\u4e0a\u8bb0\u4f4f\u6211\u3002 5 | login.username=\u7528\u6237\u540d 6 | login.password=\u5bc6\u7801 7 | login.login=\u767b\u5f55 8 | 9 | login.type.password=\u4f7f\u7528\u7528\u6237\u540d\u767b\u5f55 10 | login.type.phone=\u4f7f\u7528\u624b\u673a\u53f7\u767b\u5f55 11 | 12 | phone.number=\u624b\u673a\u53f7 13 | phone.code=\u9a8c\u8bc1\u7801 14 | phone.code.send=\u53d1\u9001\u9a8c\u8bc1\u7801 15 | -------------------------------------------------------------------------------- /spring-security-phone-core/src/main/java/org/oxerr/spring/security/phone/core/userdetails/PhoneNumberNotFoundException.java: -------------------------------------------------------------------------------- 1 | package org.oxerr.spring.security.phone.core.userdetails; 2 | 3 | import org.springframework.security.core.AuthenticationException; 4 | 5 | public class PhoneNumberNotFoundException extends AuthenticationException { 6 | 7 | private static final long serialVersionUID = 2017062701L; 8 | 9 | public PhoneNumberNotFoundException() { 10 | super("Phone number was not found."); 11 | } 12 | 13 | public PhoneNumberNotFoundException(String msg) { 14 | super(msg); 15 | } 16 | 17 | public PhoneNumberNotFoundException(String msg, Throwable t) { 18 | super(msg, t); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/site/site.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | org.apache.maven.skins 7 | maven-fluido-skin 8 | 1.5 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | true 18 | true 19 | true 20 | pull-right 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /spring-security-phone-core/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 4.0.0 9 | 10 | org.oxerr.spring.security.phone 11 | spring-security-phone 12 | 2.0.4-SNAPSHOT 13 | 14 | spring-security-phone-core 15 | Spring Security Phone Core 16 | Spring Security Phone Core. 17 | 18 | 19 | org.springframework.security 20 | spring-security-core 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /spring-security-phone-config/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 4.0.0 9 | 10 | org.oxerr.spring.security.phone 11 | spring-security-phone 12 | 2.0.4-SNAPSHOT 13 | 14 | spring-security-phone-config 15 | Spring Security Phone Config 16 | Spring Security Phone Config. 17 | 18 | 19 | org.springframework.security 20 | spring-security-config 21 | 22 | 23 | ${project.groupId} 24 | spring-security-phone-web 25 | ${project.version} 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /spring-security-phone-samples/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 4.0.0 9 | 10 | org.oxerr.spring.security.phone 11 | spring-security-phone 12 | 2.0.4-SNAPSHOT 13 | 14 | org.oxerr.spring.security.phone.samples 15 | spring-security-phone-samples 16 | pom 17 | Spring Security Phone Samples 18 | Spring Security Phone Samples. 19 | 20 | spring-security-phone-samples-helloworld 21 | 22 | 23 | 24 | 25 | org.apache.maven.plugins 26 | maven-deploy-plugin 27 | 28 | true 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /spring-security-phone-web/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 4.0.0 9 | 10 | org.oxerr.spring.security.phone 11 | spring-security-phone 12 | 2.0.4-SNAPSHOT 13 | 14 | spring-security-phone-web 15 | Spring Security Phone Web 16 | Spring Security Phone Web. 17 | 18 | 19 | javax.servlet 20 | javax.servlet-api 21 | 22 | 23 | org.springframework.security 24 | spring-security-web 25 | 26 | 27 | ${project.groupId} 28 | spring-security-phone-core 29 | ${project.version} 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /spring-security-phone-samples/spring-security-phone-samples-helloworld/src/main/java/org/oxerr/spring/security/phone/samples/helloworld/web/IndexController.java: -------------------------------------------------------------------------------- 1 | package org.oxerr.spring.security.phone.samples.helloworld.web; 2 | 3 | import static org.springframework.web.bind.annotation.RequestMethod.GET; 4 | 5 | import java.io.IOException; 6 | import java.nio.charset.StandardCharsets; 7 | import java.security.Principal; 8 | 9 | import javax.annotation.security.RolesAllowed; 10 | import javax.servlet.http.HttpServletResponse; 11 | 12 | import org.springframework.security.core.Authentication; 13 | import org.springframework.security.core.userdetails.User; 14 | import org.springframework.stereotype.Controller; 15 | import org.springframework.web.bind.annotation.RequestMapping; 16 | 17 | @Controller 18 | @RolesAllowed("ROLE_USER") 19 | public class IndexController { 20 | 21 | @RequestMapping(method = GET, path = "/") 22 | public void getIndex( 23 | Principal principal, 24 | Authentication authentication, 25 | HttpServletResponse response 26 | ) throws IOException { 27 | 28 | String username = ((User) authentication.getPrincipal()).getUsername(); 29 | 30 | response.setCharacterEncoding(StandardCharsets.UTF_8.name()); 31 | response.setContentType("text/plain"); 32 | response.getWriter().println("hello " + username); 33 | response.flushBuffer(); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Java 2 | *.class 3 | 4 | # Mobile Tools for Java (J2ME) 5 | .mtj.tmp/ 6 | 7 | # Package Files # 8 | *.jar 9 | *.war 10 | *.ear 11 | 12 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 13 | hs_err_pid* 14 | 15 | # Maven 16 | target/ 17 | pom.xml.tag 18 | pom.xml.releaseBackup 19 | pom.xml.versionsBackup 20 | pom.xml.next 21 | release.properties 22 | dependency-reduced-pom.xml 23 | buildNumber.properties 24 | .mvn/timing.properties 25 | 26 | # Eclipse 27 | .metadata 28 | bin/ 29 | tmp/ 30 | *.tmp 31 | *.bak 32 | *.swp 33 | *~.nib 34 | local.properties 35 | .settings/ 36 | .loadpath 37 | .recommenders 38 | 39 | # Eclipse Core 40 | .project 41 | 42 | # External tool builders 43 | .externalToolBuilders/ 44 | 45 | # Locally stored "Eclipse launch configurations" 46 | *.launch 47 | 48 | # PyDev specific (Python IDE for Eclipse) 49 | *.pydevproject 50 | 51 | # CDT-specific (C/C++ Development Tooling) 52 | .cproject 53 | 54 | # JDT-specific (Eclipse Java Development Tools) 55 | .classpath 56 | 57 | # Java annotation processor (APT) 58 | .factorypath 59 | 60 | # PDT-specific (PHP Development Tools) 61 | .buildpath 62 | 63 | # sbteclipse plugin 64 | .target 65 | 66 | # Tern plugin 67 | .tern-project 68 | 69 | # TeXlipse plugin 70 | .texlipse 71 | 72 | # STS (Spring Tool Suite) 73 | .springBeans 74 | 75 | # Code Recommenders 76 | .recommenders/ 77 | -------------------------------------------------------------------------------- /spring-security-phone-samples/spring-security-phone-samples-helloworld/src/main/java/org/oxerr/spring/security/phone/samples/helloworld/SecurityConfiguration.java: -------------------------------------------------------------------------------- 1 | package org.oxerr.spring.security.phone.samples.helloworld; 2 | 3 | import org.oxerr.spring.security.phone.config.annotation.web.configurers.PhoneLoginConfigurer; 4 | import org.oxerr.spring.security.phone.core.userdetails.PhoneUserDetailsService; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.context.annotation.Configuration; 7 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 8 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; 9 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 10 | 11 | @EnableWebSecurity 12 | public class SecurityConfiguration { 13 | 14 | @Configuration 15 | public static class FormLoginWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter { 16 | 17 | @Autowired 18 | private PhoneUserDetailsService phoneUserDetailsService; 19 | 20 | @Override 21 | protected void configure(HttpSecurity http) throws Exception { 22 | http 23 | .authorizeRequests() 24 | .anyRequest().permitAll() 25 | .and() 26 | .formLogin().loginPage("/login").permitAll() 27 | .and() 28 | .apply(new PhoneLoginConfigurer<>(phoneUserDetailsService)).loginProcessingUrl("/login/phone").permitAll(); 29 | } 30 | 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /spring-security-phone-core/src/main/java/org/oxerr/spring/security/phone/authentication/PhoneAuthenticationToken.java: -------------------------------------------------------------------------------- 1 | package org.oxerr.spring.security.phone.authentication; 2 | 3 | import org.springframework.security.authentication.AbstractAuthenticationToken; 4 | import org.springframework.security.core.userdetails.UserDetails; 5 | import org.springframework.util.Assert; 6 | 7 | public class PhoneAuthenticationToken extends AbstractAuthenticationToken { 8 | 9 | private static final long serialVersionUID = 2021112801L; 10 | 11 | private final Object principal; 12 | 13 | private Object credentials; 14 | 15 | public PhoneAuthenticationToken(String number, String code) { 16 | super(null); 17 | this.principal = number; 18 | this.credentials = code; 19 | setAuthenticated(false); 20 | } 21 | 22 | public PhoneAuthenticationToken(UserDetails userDetails) { 23 | super(userDetails.getAuthorities()); 24 | this.principal = userDetails; 25 | } 26 | 27 | @Override 28 | public Object getCredentials() { 29 | return this.credentials; 30 | } 31 | 32 | @Override 33 | public Object getPrincipal() { 34 | return this.principal; 35 | } 36 | 37 | @Override 38 | public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException { 39 | Assert.isTrue(!isAuthenticated, 40 | "Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead"); 41 | super.setAuthenticated(false); 42 | } 43 | 44 | @Override 45 | public void eraseCredentials() { 46 | super.eraseCredentials(); 47 | this.credentials = null; 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /spring-security-phone-samples/spring-security-phone-samples-helloworld/src/main/resources/log4j2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 11 | 12 | %d %-6p [%t] %C{1.}.%M:%L %m%n 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 25 | 26 | 27 | 28 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /spring-security-phone-samples/spring-security-phone-samples-helloworld/src/main/java/org/oxerr/spring/security/phone/samples/helloworld/WebMvcConfiguration.java: -------------------------------------------------------------------------------- 1 | package org.oxerr.spring.security.phone.samples.helloworld; 2 | 3 | import org.springframework.stereotype.Component; 4 | import org.springframework.web.servlet.config.annotation.InterceptorRegistry; 5 | import org.springframework.web.servlet.config.annotation.PathMatchConfigurer; 6 | import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; 7 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 8 | import org.springframework.web.servlet.i18n.LocaleChangeInterceptor; 9 | import org.springframework.web.util.UrlPathHelper; 10 | 11 | @Component 12 | public class WebMvcConfiguration implements WebMvcConfigurer { 13 | 14 | @Override 15 | public void configurePathMatch(PathMatchConfigurer configurer) { 16 | UrlPathHelper urlPathHelper = new UrlPathHelper(); 17 | urlPathHelper.setRemoveSemicolonContent(false); 18 | 19 | configurer.setUrlPathHelper(urlPathHelper); 20 | } 21 | 22 | @Override 23 | public void addViewControllers(ViewControllerRegistry registry) { 24 | registry.addViewController("/login").setViewName("login"); 25 | registry.addViewController("/login/phone").setViewName("login/phone"); 26 | } 27 | 28 | @Override 29 | public void addInterceptors(InterceptorRegistry registry) { 30 | LocaleChangeInterceptor localeChangeInterceptor = new LocaleChangeInterceptor(); 31 | localeChangeInterceptor.setParamName("hl"); 32 | registry.addInterceptor(localeChangeInterceptor); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /spring-security-phone-samples/spring-security-phone-samples-helloworld/src/main/resources/templates/layout.html: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 22 | 23 | 24 |
25 | 26 | 34 | 35 |
36 | 37 | 38 | -------------------------------------------------------------------------------- /spring-security-phone-samples/spring-security-phone-samples-helloworld/src/main/resources/templates/login.html: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 |
11 |
12 |
13 | 46 |
47 |
48 |
49 | 50 | 51 | -------------------------------------------------------------------------------- /spring-security-phone-samples/spring-security-phone-samples-helloworld/src/main/resources/templates/login/phone.html: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 |
12 |
13 |
14 | 47 |
48 |
49 |
50 | 51 | 52 | -------------------------------------------------------------------------------- /spring-security-phone-coverage/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 4.0.0 7 | 8 | org.oxerr.spring.security.phone 9 | spring-security-phone 10 | 2.0.4-SNAPSHOT 11 | 12 | spring-security-phone-coverage 13 | Spring Security Phone Coverage 14 | Compute aggregated test code coverage. 15 | 16 | true 17 | true 18 | true 19 | true 20 | 21 | 22 | 23 | 24 | org.apache.maven.plugins 25 | maven-jar-plugin 26 | 27 | true 28 | 29 | 30 | 31 | 32 | 33 | 34 | ${project.groupId} 35 | spring-security-phone-core 36 | ${project.version} 37 | 38 | 39 | ${project.groupId} 40 | spring-security-phone-web 41 | ${project.version} 42 | 43 | 44 | ${project.groupId} 45 | spring-security-phone-config 46 | ${project.version} 47 | 48 | 49 | org.oxerr.spring.security.phone.samples 50 | spring-security-phone-samples-helloworld 51 | ${project.version} 52 | 53 | 54 | 55 | 56 | 57 | org.jacoco 58 | jacoco-maven-plugin 59 | 60 | 61 | 62 | report-aggregate 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /spring-security-phone-web/src/main/java/org/oxerr/spring/security/phone/web/authentication/PhoneAuthenticationFilter.java: -------------------------------------------------------------------------------- 1 | package org.oxerr.spring.security.phone.web.authentication; 2 | 3 | import javax.servlet.http.HttpServletRequest; 4 | import javax.servlet.http.HttpServletResponse; 5 | 6 | import org.oxerr.spring.security.phone.authentication.PhoneAuthenticationToken; 7 | import org.springframework.security.authentication.AuthenticationServiceException; 8 | import org.springframework.security.core.Authentication; 9 | import org.springframework.security.core.AuthenticationException; 10 | import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter; 11 | import org.springframework.security.web.util.matcher.AntPathRequestMatcher; 12 | import org.springframework.util.Assert; 13 | 14 | public class PhoneAuthenticationFilter 15 | extends AbstractAuthenticationProcessingFilter { 16 | 17 | public static final String SPRING_SECURITY_FORM_NUMBER_KEY = "number"; 18 | public static final String SPRING_SECURITY_FORM_CODE_KEY = "code"; 19 | 20 | private String numberParameter = SPRING_SECURITY_FORM_NUMBER_KEY; 21 | private String codeParameter = SPRING_SECURITY_FORM_CODE_KEY; 22 | 23 | public PhoneAuthenticationFilter() { 24 | super(new AntPathRequestMatcher("/login/phone", "POST")); 25 | } 26 | 27 | /** 28 | * {@inheritDoc} 29 | */ 30 | @Override 31 | public Authentication attemptAuthentication(HttpServletRequest request, 32 | HttpServletResponse response) throws AuthenticationException { 33 | if (!request.getMethod().equals("POST")) { 34 | throw new AuthenticationServiceException( 35 | "Authentication method not supported: " + request.getMethod()); 36 | } 37 | 38 | final String number = request.getParameter(numberParameter); 39 | final String code = request.getParameter(codeParameter); 40 | 41 | final PhoneAuthenticationToken authRequest = new PhoneAuthenticationToken(number, code); 42 | authRequest.setDetails(authenticationDetailsSource.buildDetails(request)); 43 | 44 | return this.getAuthenticationManager().authenticate(authRequest); 45 | } 46 | 47 | public void setNumberParameter(String numberParameter) { 48 | Assert.hasText(numberParameter, "Number parameter must not be empty or null"); 49 | this.numberParameter = numberParameter; 50 | } 51 | 52 | public void setCodeParameter(String codeParameter) { 53 | Assert.hasText(codeParameter, "Code parameter must not be empty or null"); 54 | this.codeParameter = codeParameter; 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /spring-security-phone-samples/spring-security-phone-samples-helloworld/src/main/java/org/oxerr/spring/security/phone/samples/helloworld/Application.java: -------------------------------------------------------------------------------- 1 | package org.oxerr.spring.security.phone.samples.helloworld; 2 | 3 | import java.util.Arrays; 4 | 5 | import org.oxerr.spring.security.phone.core.userdetails.PhoneNumberNotFoundException; 6 | import org.oxerr.spring.security.phone.core.userdetails.PhoneUserDetailsService; 7 | import org.springframework.boot.SpringApplication; 8 | import org.springframework.boot.autoconfigure.SpringBootApplication; 9 | import org.springframework.boot.web.servlet.FilterRegistrationBean; 10 | import org.springframework.context.annotation.Bean; 11 | import org.springframework.core.Ordered; 12 | import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; 13 | import org.springframework.security.core.authority.SimpleGrantedAuthority; 14 | import org.springframework.security.core.userdetails.User; 15 | import org.springframework.security.core.userdetails.UserDetails; 16 | import org.springframework.web.filter.CommonsRequestLoggingFilter; 17 | 18 | @SpringBootApplication 19 | @EnableGlobalMethodSecurity(jsr250Enabled = true) 20 | public class Application { 21 | 22 | /** 23 | * Writes the request URI (and optionally the query string) to the Commons Log. 24 | * 25 | * @return the filter registration bean for the {@link CommonsRequestLoggingFilter}. 26 | */ 27 | @Bean 28 | public FilterRegistrationBean commonsRequestLoggingFilterRegistrationBean() { 29 | FilterRegistrationBean registrationBean = new FilterRegistrationBean<>(); 30 | CommonsRequestLoggingFilter requestLoggingFilter = new CommonsRequestLoggingFilter(); 31 | registrationBean.setFilter(requestLoggingFilter); 32 | registrationBean.setOrder(Ordered.HIGHEST_PRECEDENCE); 33 | return registrationBean; 34 | } 35 | 36 | @Bean 37 | public PhoneUserDetailsService phoneUserDetailsService() { 38 | return new PhoneUserDetailsService() { 39 | 40 | @Override 41 | public UserDetails loadUserByNumber(String number) 42 | throws PhoneNumberNotFoundException { 43 | return new User("user", "user1234", Arrays.asList(new SimpleGrantedAuthority("ROLE_USER"))); 44 | } 45 | 46 | @Override 47 | public boolean consumeCode(String number, String code) { 48 | return "123456".equals(code); 49 | } 50 | }; 51 | } 52 | 53 | public static void main(String[] args) { 54 | SpringApplication.run(Application.class, args); 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /spring-security-phone-samples/spring-security-phone-samples-helloworld/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 4.0.0 9 | 10 | org.oxerr.spring.security.phone.samples 11 | spring-security-phone-samples 12 | 2.0.4-SNAPSHOT 13 | 14 | spring-security-phone-samples-helloworld 15 | 16 | 2.6.0 17 | 18 | 19 | 20 | 21 | org.springframework.boot 22 | spring-boot-dependencies 23 | ${spring-boot.version} 24 | pom 25 | import 26 | 27 | 28 | 29 | 30 | 31 | org.springframework.boot 32 | spring-boot-configuration-processor 33 | true 34 | 35 | 36 | org.springframework.boot 37 | spring-boot-devtools 38 | true 39 | 40 | 41 | org.springframework.boot 42 | spring-boot-starter 43 | 44 | 45 | org.springframework.boot 46 | spring-boot-starter-logging 47 | 48 | 49 | 50 | 51 | org.springframework.boot 52 | spring-boot-starter-log4j2 53 | 54 | 55 | org.springframework.boot 56 | spring-boot-starter-thymeleaf 57 | 58 | 59 | org.springframework.boot 60 | spring-boot-starter-web 61 | 62 | 63 | nz.net.ultraq.thymeleaf 64 | thymeleaf-layout-dialect 65 | 66 | 67 | org.oxerr.spring.security.phone 68 | spring-security-phone-config 69 | ${project.version} 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /spring-security-phone-core/src/main/java/org/oxerr/spring/security/phone/authentication/PhoneAuthenticationProvider.java: -------------------------------------------------------------------------------- 1 | package org.oxerr.spring.security.phone.authentication; 2 | 3 | import org.oxerr.spring.security.phone.core.userdetails.PhoneUserDetailsService; 4 | import org.springframework.beans.factory.InitializingBean; 5 | import org.springframework.context.MessageSource; 6 | import org.springframework.context.MessageSourceAware; 7 | import org.springframework.context.support.MessageSourceAccessor; 8 | import org.springframework.security.authentication.AccountStatusUserDetailsChecker; 9 | import org.springframework.security.authentication.AuthenticationProvider; 10 | import org.springframework.security.authentication.BadCredentialsException; 11 | import org.springframework.security.core.Authentication; 12 | import org.springframework.security.core.AuthenticationException; 13 | import org.springframework.security.core.SpringSecurityMessageSource; 14 | import org.springframework.security.core.userdetails.UserDetails; 15 | import org.springframework.security.core.userdetails.UserDetailsChecker; 16 | import org.springframework.util.Assert; 17 | 18 | public class PhoneAuthenticationProvider implements AuthenticationProvider, 19 | InitializingBean, MessageSourceAware { 20 | 21 | protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor(); 22 | private final PhoneUserDetailsService phoneUserDetailsService; 23 | 24 | private UserDetailsChecker postAuthenticationChecks = new AccountStatusUserDetailsChecker(); 25 | 26 | public PhoneAuthenticationProvider( 27 | PhoneUserDetailsService phoneUserDetailsService 28 | ) { 29 | this.phoneUserDetailsService = phoneUserDetailsService; 30 | } 31 | 32 | /** 33 | * {@inheritDoc} 34 | */ 35 | @Override 36 | public Authentication authenticate(final Authentication authentication) 37 | throws AuthenticationException { 38 | final PhoneAuthenticationToken phoneAuthenticationToken = (PhoneAuthenticationToken) authentication; 39 | 40 | final String number = (String) phoneAuthenticationToken.getPrincipal(); 41 | final String code = (String) phoneAuthenticationToken.getCredentials(); 42 | 43 | if (!phoneUserDetailsService.consumeCode(number, code)) { 44 | throw new BadCredentialsException(messages.getMessage( 45 | "AbstractUserDetailsAuthenticationProvider.badCredentials", 46 | "Bad credentials")); 47 | } 48 | 49 | UserDetails user = phoneUserDetailsService.loadUserByNumber(number); 50 | 51 | postAuthenticationChecks.check(user); 52 | 53 | return new PhoneAuthenticationToken(user); 54 | } 55 | 56 | /** 57 | * {@inheritDoc} 58 | */ 59 | @Override 60 | public boolean supports(Class authentication) { 61 | return PhoneAuthenticationToken.class.isAssignableFrom(authentication); 62 | } 63 | 64 | /** 65 | * {@inheritDoc} 66 | */ 67 | @Override 68 | public void afterPropertiesSet() throws Exception { 69 | Assert.notNull(this.messages, "A message source must be set"); 70 | Assert.notNull(this.phoneUserDetailsService, "A phoneUserDetailsService must be set."); 71 | Assert.notNull(this.postAuthenticationChecks, "A postAuthenticationChecks must be set."); 72 | } 73 | 74 | /** 75 | * {@inheritDoc} 76 | */ 77 | @Override 78 | public void setMessageSource(MessageSource messageSource) { 79 | this.messages = new MessageSourceAccessor(messageSource); 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /spring-security-phone-config/src/main/java/org/oxerr/spring/security/phone/config/annotation/web/configurers/PhoneLoginConfigurer.java: -------------------------------------------------------------------------------- 1 | package org.oxerr.spring.security.phone.config.annotation.web.configurers; 2 | 3 | import org.oxerr.spring.security.phone.authentication.PhoneAuthenticationProvider; 4 | import org.oxerr.spring.security.phone.core.userdetails.PhoneUserDetailsService; 5 | import org.oxerr.spring.security.phone.web.authentication.PhoneAuthenticationFilter; 6 | import org.springframework.security.config.annotation.web.HttpSecurityBuilder; 7 | import org.springframework.security.config.annotation.web.configurers.AbstractAuthenticationFilterConfigurer; 8 | import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; 9 | import org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter; 10 | import org.springframework.security.web.util.matcher.AntPathRequestMatcher; 11 | import org.springframework.security.web.util.matcher.RequestMatcher; 12 | 13 | public final class PhoneLoginConfigurer> 14 | extends 15 | AbstractAuthenticationFilterConfigurer, 16 | PhoneAuthenticationFilter> { 17 | 18 | private final PhoneUserDetailsService phoneUserDetailsService; 19 | 20 | public PhoneLoginConfigurer(PhoneUserDetailsService phoneUserDetailsService) { 21 | super(new PhoneAuthenticationFilter(), "/login/phone"); 22 | this.phoneUserDetailsService = phoneUserDetailsService; 23 | numberParameter("number"); 24 | codeParameter("code"); 25 | } 26 | 27 | /** 28 | * {@inheritDoc} 29 | */ 30 | @Override 31 | public void configure(H http) throws Exception { 32 | 33 | // Make sure the filter be registered in 34 | // org.springframework.security.config.annotation.web.builders.FilterComparator 35 | http.addFilterAfter(getAuthenticationFilter(), 36 | UsernamePasswordAuthenticationFilter.class); 37 | 38 | super.configure(http); 39 | } 40 | 41 | @Override 42 | public PhoneLoginConfigurer loginPage(String loginPage) { 43 | return super.loginPage(loginPage); 44 | } 45 | 46 | /** 47 | * {@inheritDoc} 48 | */ 49 | @Override 50 | public void init(H http) throws Exception { 51 | super.init(http); 52 | 53 | PhoneAuthenticationProvider authenticationProvider = new PhoneAuthenticationProvider( 54 | phoneUserDetailsService); 55 | postProcess(authenticationProvider); 56 | http.authenticationProvider(authenticationProvider); 57 | 58 | initDefaultLoginFilter(http); 59 | } 60 | 61 | /** 62 | * {@inheritDoc} 63 | */ 64 | @Override 65 | protected RequestMatcher createLoginProcessingUrlMatcher( 66 | String loginProcessingUrl) { 67 | return new AntPathRequestMatcher(loginProcessingUrl, "POST"); 68 | } 69 | 70 | private void initDefaultLoginFilter(H http) { 71 | DefaultLoginPageGeneratingFilter loginPageGeneratingFilter = http 72 | .getSharedObject(DefaultLoginPageGeneratingFilter.class); 73 | if (loginPageGeneratingFilter != null && !isCustomLoginPage()) { 74 | String loginPageUrl = loginPageGeneratingFilter.getLoginPageUrl(); 75 | if (loginPageUrl == null) { 76 | loginPageGeneratingFilter.setLoginPageUrl(getLoginPage()); 77 | loginPageGeneratingFilter.setFailureUrl(getFailureUrl()); 78 | } 79 | } 80 | } 81 | 82 | public PhoneLoginConfigurer numberParameter(String numberParameter) { 83 | getAuthenticationFilter().setNumberParameter(numberParameter); 84 | return this; 85 | } 86 | 87 | public PhoneLoginConfigurer codeParameter(String codeParameter) { 88 | getAuthenticationFilter().setCodeParameter(codeParameter); 89 | return this; 90 | } 91 | 92 | } 93 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 4.0.0 9 | 10 | org.oxerr 11 | oxerr-parent 12 | 1.4.0 13 | 14 | org.oxerr.spring.security.phone 15 | spring-security-phone 16 | 2.0.4-SNAPSHOT 17 | pom 18 | Spring Security Phone 19 | Login via phone number & verification code. 20 | https://oxerr.org/spring-security-phone 21 | 2016 22 | 23 | 1.8 24 | 1.8 25 | 5.7.14 26 | 27 | 28 | spring-security-phone-core 29 | spring-security-phone-web 30 | spring-security-phone-config 31 | spring-security-phone-samples 32 | spring-security-phone-coverage 33 | 34 | 35 | 36 | 37 | org.codehaus.mojo 38 | versions-maven-plugin 39 | 40 | file:///${session.executionRootDirectory}/maven-version-rules.xml 41 | 42 | 43 | 44 | org.jacoco 45 | jacoco-maven-plugin 46 | 47 | 48 | 49 | prepare-agent 50 | 51 | 52 | 53 | report 54 | verify 55 | 56 | report 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | org.codehaus.mojo 66 | findbugs-maven-plugin 67 | 3.0.3 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | org.codehaus.mojo 76 | findbugs-maven-plugin 77 | 78 | 79 | 80 | 81 | 82 | 83 | javax.servlet 84 | javax.servlet-api 85 | 4.0.1 86 | 87 | 88 | org.springframework.security 89 | spring-security-core 90 | ${spring-security.version} 91 | 92 | 93 | org.springframework.security 94 | spring-security-config 95 | ${spring-security.version} 96 | 97 | 98 | org.springframework.security 99 | spring-security-web 100 | ${spring-security.version} 101 | 102 | 103 | 104 | 105 | scm:git:git@github.com:sutra/spring-security-phone.git 106 | scm:git:git@github.com:sutra/spring-security-phone.git 107 | https://github.com/sutra/spring-security-phone/tree/master 108 | 109 | 110 | GitHub 111 | https://github.com/sutra/spring-security-phone/issues 112 | 113 | 114 | 115 | oxerr.org 116 | scp://oxerr.org/usr/local/www/spring-security-phone/ 117 | 118 | 119 | 120 | --------------------------------------------------------------------------------