From 55b09a6a3bc4a21201189d855e140308f05016fb Mon Sep 17 00:00:00 2001 From: Vitaly Takmazov Date: Fri, 25 Nov 2016 13:20:15 +0300 Subject: juick-api: security WIP --- juick-api/build.gradle | 5 -- .../juick/api/configuration/ApiInitializer.java | 2 +- .../juick/api/configuration/ApiSecurityConfig.java | 79 ++++++++++++++++++++++ .../api/configuration/ApiSecurityInitializer.java | 10 +++ .../java/com/juick/api/controllers/Messages.java | 22 ++---- juick-api/src/main/webapp/WEB-INF/web.xml | 1 + .../java/com/juick/api/tests/MessagesTests.java | 3 +- juick-server/build.gradle | 7 ++ .../security/JuickAuthenticationEntryPoint.java | 20 ++++++ .../security/JuickAuthenticationProvider.java | 35 ++++++++++ .../juick/server/security/entities/JuickUser.java | 62 +++++++++++++++++ juick-spring-www/build.gradle | 3 - .../juick/www/configuration/WebSecurityConfig.java | 2 +- .../main/java/com/juick/www/entity/JuickUser.java | 62 ----------------- 14 files changed, 223 insertions(+), 90 deletions(-) create mode 100644 juick-api/src/main/java/com/juick/api/configuration/ApiSecurityConfig.java create mode 100644 juick-api/src/main/java/com/juick/api/configuration/ApiSecurityInitializer.java create mode 100644 juick-server/src/main/java/com/juick/server/security/JuickAuthenticationEntryPoint.java create mode 100644 juick-server/src/main/java/com/juick/server/security/JuickAuthenticationProvider.java create mode 100644 juick-server/src/main/java/com/juick/server/security/entities/JuickUser.java delete mode 100644 juick-spring-www/src/main/java/com/juick/www/entity/JuickUser.java diff --git a/juick-api/build.gradle b/juick-api/build.gradle index c90b22d2..f669be7e 100644 --- a/juick-api/build.gradle +++ b/juick-api/build.gradle @@ -10,17 +10,12 @@ apply plugin: 'org.akhikhl.gretty' apply plugin: 'com.github.ben-manes.versions' def springFrameworkVersion = "4.3.4.RELEASE" -def springSecurityVersion = "4.2.0.RELEASE" dependencies { compile project(':juick-server') compile "org.springframework:spring-webmvc:${springFrameworkVersion}" - compile "org.springframework.security:spring-security-web:${springSecurityVersion}" - compile "org.springframework.security:spring-security-config:${springSecurityVersion}" - - providedCompile 'javax.servlet:javax.servlet-api:3.1.0' compile 'com.github.pengrad:java-telegram-bot-api:2.3.0' compile 'org.msbotframework4j:msbotframework4j-builder:0.1.4' compile 'com.neovisionaries:nv-websocket-client:1.30' diff --git a/juick-api/src/main/java/com/juick/api/configuration/ApiInitializer.java b/juick-api/src/main/java/com/juick/api/configuration/ApiInitializer.java index f5ba4ff1..2dc25e66 100644 --- a/juick-api/src/main/java/com/juick/api/configuration/ApiInitializer.java +++ b/juick-api/src/main/java/com/juick/api/configuration/ApiInitializer.java @@ -17,7 +17,7 @@ public class ApiInitializer extends AbstractAnnotationConfigDispatcherServletIni @Override protected Class[] getServletConfigClasses() { - return new Class[]{ApiMvcConfiguration.class}; + return new Class[]{ApiMvcConfiguration.class, ApiSecurityConfig.class}; } @Override diff --git a/juick-api/src/main/java/com/juick/api/configuration/ApiSecurityConfig.java b/juick-api/src/main/java/com/juick/api/configuration/ApiSecurityConfig.java new file mode 100644 index 00000000..c0043950 --- /dev/null +++ b/juick-api/src/main/java/com/juick/api/configuration/ApiSecurityConfig.java @@ -0,0 +1,79 @@ +package com.juick.api.configuration; + +import com.juick.server.security.JuickAuthenticationEntryPoint; +import com.juick.server.security.JuickAuthenticationProvider; +import com.juick.server.security.entities.JuickUser; +import com.juick.service.UserService; +import org.apache.commons.lang3.StringUtils; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.PropertySource; +import org.springframework.core.env.Environment; +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.builders.WebSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; + +import javax.annotation.Resource; +import javax.inject.Inject; + +/** + * Created by aalexeev on 11/21/16. + */ +@Configuration +@EnableWebSecurity +@PropertySource("classpath:juick.conf") +public class ApiSecurityConfig extends WebSecurityConfigurerAdapter { + @Resource + private Environment env; + @Resource + private UserService userService; + + protected ApiSecurityConfig() { + super(true); + } + + @Bean + public JuickAuthenticationEntryPoint getBasicAuthEntryPoint(){ + return new JuickAuthenticationEntryPoint(); + } + + @Bean("userDetailsService") + @Override + public UserDetailsService userDetailsServiceBean() throws Exception { + return username -> { + if (StringUtils.isBlank(username)) + throw new UsernameNotFoundException("Invalid user name " + username); + + com.juick.User user = userService.getUserByName(username); + + if (user != null) + return new JuickUser(user); + + throw new UsernameNotFoundException("The username " + username + " is not found"); + }; + } + + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .authorizeRequests() + .antMatchers("/home").hasRole("USER") + .and().httpBasic().authenticationEntryPoint(new JuickAuthenticationEntryPoint()) + .and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); + } + + @Inject + public void configureGlobalSecurity(AuthenticationManagerBuilder auth) throws Exception { + auth.authenticationProvider(new JuickAuthenticationProvider()); + } + @Override + public void configure(WebSecurity web) throws Exception { + web.ignoring().antMatchers(HttpMethod.OPTIONS, "/**"); + } +} diff --git a/juick-api/src/main/java/com/juick/api/configuration/ApiSecurityInitializer.java b/juick-api/src/main/java/com/juick/api/configuration/ApiSecurityInitializer.java new file mode 100644 index 00000000..295e367c --- /dev/null +++ b/juick-api/src/main/java/com/juick/api/configuration/ApiSecurityInitializer.java @@ -0,0 +1,10 @@ +package com.juick.api.configuration; + +/** + * Created by vitalyster on 25.11.2016. + */ +import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer; + +public class ApiSecurityInitializer extends AbstractSecurityWebApplicationInitializer { + +} diff --git a/juick-api/src/main/java/com/juick/api/controllers/Messages.java b/juick-api/src/main/java/com/juick/api/controllers/Messages.java index f4cde321..36882140 100644 --- a/juick-api/src/main/java/com/juick/api/controllers/Messages.java +++ b/juick-api/src/main/java/com/juick/api/controllers/Messages.java @@ -16,7 +16,6 @@ import org.slf4j.LoggerFactory; import org.springframework.http.MediaType; import org.springframework.stereotype.Controller; import org.springframework.util.StringUtils; -import org.springframework.web.bind.annotation.CrossOrigin; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; @@ -25,6 +24,7 @@ import rocks.xmpp.core.stanza.model.Message; import javax.inject.Inject; import javax.servlet.http.HttpServletRequest; +import java.security.Principal; import java.util.List; /** @@ -47,22 +47,10 @@ public class Messages { // TODO: serialize image urls @RequestMapping(value = "/home", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE) - public List doGetHome(HttpServletRequest request) { - // TODO: use spring-security - String auth = request.getHeader("Authorization"); - int vuid = userService.getUIDByHttpAuth(auth); - if (vuid == -1) { - throw new HttpForbiddenException(); - } - if (vuid == 0) { - String hash = request.getParameter("hash"); - if (hash != null && hash.length() == 16) { - vuid = userService.getUIDbyHash(hash); - } - } - if (vuid == 0) { - throw new HttpForbiddenException(); - } + public List doGetHome(HttpServletRequest request, Principal principal) { + String name = principal.getName(); + User visitor = userService.getUserByName(name); + int vuid = visitor.getUid(); int before_mid = NumberUtils.toInt(request.getParameter("before_mid"), 0); List mids = messagesService.getMyFeed(vuid, before_mid); return messagesService.getMessages(mids); diff --git a/juick-api/src/main/webapp/WEB-INF/web.xml b/juick-api/src/main/webapp/WEB-INF/web.xml index 7b33aefc..7e1c30d0 100644 --- a/juick-api/src/main/webapp/WEB-INF/web.xml +++ b/juick-api/src/main/webapp/WEB-INF/web.xml @@ -1,3 +1,4 @@ + diff --git a/juick-api/src/test/java/com/juick/api/tests/MessagesTests.java b/juick-api/src/test/java/com/juick/api/tests/MessagesTests.java index 9f324686..1dec4b7c 100644 --- a/juick-api/src/test/java/com/juick/api/tests/MessagesTests.java +++ b/juick-api/src/test/java/com/juick/api/tests/MessagesTests.java @@ -5,6 +5,7 @@ import com.juick.Tag; import com.juick.User; import com.juick.api.configuration.ApiAppConfiguration; import com.juick.api.configuration.ApiMvcConfiguration; +import com.juick.api.configuration.ApiSecurityConfig; import com.juick.service.MessagesService; import com.juick.service.UserService; import org.junit.Before; @@ -42,7 +43,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. @WebAppConfiguration public class MessagesTests { @Configuration - @Import(value={ApiMvcConfiguration.class, ApiAppConfiguration.class}) + @Import(value={ApiMvcConfiguration.class, ApiAppConfiguration.class, ApiSecurityConfig.class}) static class Config { @Bean @Primary diff --git a/juick-server/build.gradle b/juick-server/build.gradle index 9f7db721..7acf4a7e 100644 --- a/juick-server/build.gradle +++ b/juick-server/build.gradle @@ -1,4 +1,5 @@ apply plugin: 'java' +apply plugin: 'war' apply plugin: 'com.github.ben-manes.versions' sourceCompatibility = 1.8 @@ -7,6 +8,7 @@ def jacksonVersion = '2.8.5' def logbackVersion = '1.1.7' def slf4jVersion = '1.7.21' def springFrameworkVersion = '4.3.4.RELEASE' +def springSecurityVersion = "4.2.0.RELEASE" dependencies { compile project(':juick-core') @@ -32,6 +34,11 @@ dependencies { compile "org.springframework:spring-context:${springFrameworkVersion}" compile "org.springframework:spring-jdbc:${springFrameworkVersion}" + providedCompile 'javax.servlet:javax.servlet-api:3.1.0' + + compile "org.springframework.security:spring-security-web:${springSecurityVersion}" + compile "org.springframework.security:spring-security-config:${springSecurityVersion}" + compile 'org.apache.commons:commons-dbcp2:2.1.1' compile 'com.googlecode.log4jdbc:log4jdbc:1.2' compile 'org.json:json:20160810' diff --git a/juick-server/src/main/java/com/juick/server/security/JuickAuthenticationEntryPoint.java b/juick-server/src/main/java/com/juick/server/security/JuickAuthenticationEntryPoint.java new file mode 100644 index 00000000..4c73196d --- /dev/null +++ b/juick-server/src/main/java/com/juick/server/security/JuickAuthenticationEntryPoint.java @@ -0,0 +1,20 @@ +package com.juick.server.security; + +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.AuthenticationEntryPoint; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +/** + * Created by vitalyster on 25.11.2016. + */ +public class JuickAuthenticationEntryPoint implements AuthenticationEntryPoint { + @Override + public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) + throws IOException, ServletException { + response.setStatus(HttpServletResponse.SC_FORBIDDEN); + } +} diff --git a/juick-server/src/main/java/com/juick/server/security/JuickAuthenticationProvider.java b/juick-server/src/main/java/com/juick/server/security/JuickAuthenticationProvider.java new file mode 100644 index 00000000..bf0ed4d7 --- /dev/null +++ b/juick-server/src/main/java/com/juick/server/security/JuickAuthenticationProvider.java @@ -0,0 +1,35 @@ +package com.juick.server.security; + +import com.juick.service.UserService; +import org.springframework.security.authentication.AuthenticationProvider; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.authority.SimpleGrantedAuthority; + +import javax.inject.Inject; +import java.util.Collections; + +/** + * Created by vitalyster on 25.11.2016. + */ +public class JuickAuthenticationProvider implements AuthenticationProvider { + @Inject + UserService userService; + @Override + public Authentication authenticate(Authentication authentication) throws AuthenticationException { + String name = authentication.getName(); + String password = authentication.getCredentials().toString(); + if (userService.checkPassword(name, password) > 0) { + return new UsernamePasswordAuthenticationToken(name, password, Collections.singletonList( + new SimpleGrantedAuthority("ROLE_USER") + )); + } + return null; + } + + @Override + public boolean supports(Class authentication) { + return authentication.equals(UsernamePasswordAuthenticationToken.class); + } +} diff --git a/juick-server/src/main/java/com/juick/server/security/entities/JuickUser.java b/juick-server/src/main/java/com/juick/server/security/entities/JuickUser.java new file mode 100644 index 00000000..6cc002ae --- /dev/null +++ b/juick-server/src/main/java/com/juick/server/security/entities/JuickUser.java @@ -0,0 +1,62 @@ +package com.juick.server.security.entities; + +import com.juick.User; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; + +import java.util.Collection; +import java.util.Collections; + +/** + * Created by aalexeev on 11/21/16. + */ +public class JuickUser implements UserDetails { + private static final GrantedAuthority ROLE_USER = new SimpleGrantedAuthority("ROLE_USER"); + + private final com.juick.User user; + + + public JuickUser(com.juick.User user) { + this.user = user; + } + + @Override + public Collection getAuthorities() { + return Collections.singletonList(ROLE_USER); + } + + @Override + public String getPassword() { + return null; + } + + @Override + public String getUsername() { + return user.getName(); + } + + @Override + public boolean isAccountNonExpired() { + return true; + } + + @Override + public boolean isAccountNonLocked() { + return false; + } + + @Override + public boolean isCredentialsNonExpired() { + return true; + } + + @Override + public boolean isEnabled() { + return !user.isBanned(); + } + + public User getUser() { + return user; + } +} diff --git a/juick-spring-www/build.gradle b/juick-spring-www/build.gradle index 593deb55..14b3bb9c 100644 --- a/juick-spring-www/build.gradle +++ b/juick-spring-www/build.gradle @@ -20,7 +20,6 @@ apply plugin: 'com.github.ben-manes.versions' def springFrameworkVersion = "4.3.4.RELEASE" def thymeleafVersion = "3.0.2.RELEASE" -def springSecurityVersion = "4.2.0.RELEASE" dependencies { compile project(':juick-server') @@ -32,8 +31,6 @@ dependencies { compile "org.thymeleaf.extras:thymeleaf-extras-java8time:3.0.0.RELEASE" compile "org.thymeleaf.extras:thymeleaf-extras-springsecurity4:3.0.1.RELEASE" compile "nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect:2.1.1" - compile "org.springframework.security:spring-security-web:${springSecurityVersion}" - compile "org.springframework.security:spring-security-config:${springSecurityVersion}" testCompile "junit:junit:4.12" testCompile "org.mockito:mockito-core:1.+" diff --git a/juick-spring-www/src/main/java/com/juick/www/configuration/WebSecurityConfig.java b/juick-spring-www/src/main/java/com/juick/www/configuration/WebSecurityConfig.java index dcbd5459..3753a4aa 100644 --- a/juick-spring-www/src/main/java/com/juick/www/configuration/WebSecurityConfig.java +++ b/juick-spring-www/src/main/java/com/juick/www/configuration/WebSecurityConfig.java @@ -1,7 +1,7 @@ package com.juick.www.configuration; import com.juick.service.UserService; -import com.juick.www.entity.JuickUser; +import com.juick.server.security.entities.JuickUser; import org.apache.commons.lang3.StringUtils; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.PropertySource; diff --git a/juick-spring-www/src/main/java/com/juick/www/entity/JuickUser.java b/juick-spring-www/src/main/java/com/juick/www/entity/JuickUser.java deleted file mode 100644 index 20d7889d..00000000 --- a/juick-spring-www/src/main/java/com/juick/www/entity/JuickUser.java +++ /dev/null @@ -1,62 +0,0 @@ -package com.juick.www.entity; - -import com.juick.User; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.authority.SimpleGrantedAuthority; -import org.springframework.security.core.userdetails.UserDetails; - -import java.util.Collection; -import java.util.Collections; - -/** - * Created by aalexeev on 11/21/16. - */ -public class JuickUser implements UserDetails { - private static final GrantedAuthority ROLE_USER = new SimpleGrantedAuthority("ROLE_USER"); - - private final com.juick.User user; - - - public JuickUser(com.juick.User user) { - this.user = user; - } - - @Override - public Collection getAuthorities() { - return Collections.singletonList(ROLE_USER); - } - - @Override - public String getPassword() { - return null; - } - - @Override - public String getUsername() { - return user.getName(); - } - - @Override - public boolean isAccountNonExpired() { - return true; - } - - @Override - public boolean isAccountNonLocked() { - return false; - } - - @Override - public boolean isCredentialsNonExpired() { - return true; - } - - @Override - public boolean isEnabled() { - return !user.isBanned(); - } - - public User getUser() { - return user; - } -} -- cgit v1.2.3