* BAEL-509: Initial Commit - working but needs a few fixes to REST API, etc.

* Fixed Authentication Failure - added subscription handlers - sufficient for Websocket Authentication/Authorization - still some issues to resolve with subscriptions and REST API

* Final version

* CSRF token controller - cleanup of chat wrapper
This commit is contained in:
Adam InTae Gerard
2017-07-03 21:57:27 -05:00
committed by KevinGilmore
parent 70ae331cd7
commit db2bb252de
47 changed files with 1654 additions and 0 deletions
+1
View File
@@ -191,6 +191,7 @@
<module>spring-security-mvc-login</module>
<module>spring-security-mvc-persisted-remember-me</module>
<module>spring-security-mvc-session</module>
<module>spring-security-mvc-socket</module>
<module>spring-security-rest-basic-auth</module>
<module>spring-security-rest-custom</module>
<module>spring-security-rest-full</module>
+5
View File
@@ -0,0 +1,5 @@
node_modules
.idea
target
*.iml
out
+2
View File
@@ -0,0 +1,2 @@
### Relevant Articles:
- [Intro to Security and WebSockets](http://www.baeldung.com/intro-to-security-and-websockets)
+181
View File
@@ -0,0 +1,181 @@
<?xml version="1.0"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>com.baeldung.springsecuredsockets</groupId>
<artifactId>spring-security-mvc-socket</artifactId>
<packaging>war</packaging>
<version>1.0.0</version>
<name>spring-security-mvc-socket</name>
<properties>
<springframework.version>4.3.8.RELEASE</springframework.version>
<springsecurity.version>4.2.3.RELEASE</springsecurity.version>
<jackson.version>2.8.7</jackson.version>
<slf4j.version>1.7.25</slf4j.version>
</properties>
<dependencies>
<!-- Spring Dependencies -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${springframework.version}</version>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${springframework.version}</version>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${springframework.version}</version>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Spring Security -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>${springsecurity.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>${springsecurity.version}</version>
</dependency>
<!-- Data -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>1.11.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.2.10.Final</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.196</version>
</dependency>
<!-- Websockets -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-websocket</artifactId>
<version>${springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-messaging</artifactId>
<version>${springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-messaging</artifactId>
<version>${springsecurity.version}</version>
</dependency>
<!-- Logging -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<!-- Servlet -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp.jstl</groupId>
<artifactId>jstl-api</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.1</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!-- Jackson Dependencies -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>${jackson.version}</version>
</dependency>
</dependencies>
<build>
<pluginManagement>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<path>/spring-security-mvc-socket</path>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.0.0</version>
<configuration>
<warSourceDirectory>src/main/webapp</warSourceDirectory>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
</plugins>
</pluginManagement>
<finalName>spring-security-mvc-socket</finalName>
</build>
</project>
@@ -0,0 +1,56 @@
package com.baeldung.springsecuredsockets.config;
import org.h2.tools.Server;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
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.resource.PathResourceResolver;
import org.springframework.web.servlet.view.JstlView;
import org.springframework.web.servlet.view.UrlBasedViewResolver;
import java.sql.SQLException;
@Configuration
@EnableWebMvc
@EnableJpaRepositories
@ComponentScan("com.baeldung.springsecuredsockets")
@Import({ SecurityConfig.class, DataStoreConfig.class, SocketBrokerConfig.class, SocketSecurityConfig.class })
public class AppConfig extends WebMvcConfigurerAdapter {
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("index");
registry.addViewController("/login").setViewName("login");
registry.addViewController("/secured/socket").setViewName("socket");
registry.addViewController("/secured/success").setViewName("success");
registry.addViewController("/denied").setViewName("denied");
}
@Bean
public UrlBasedViewResolver viewResolver() {
final UrlBasedViewResolver bean = new UrlBasedViewResolver();
bean.setPrefix("/WEB-INF/jsp/");
bean.setSuffix(".jsp");
bean.setViewClass(JstlView.class);
return bean;
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**")
.addResourceLocations("/", "/resources/")
.setCachePeriod(3600)
.resourceChain(true)
.addResolver(new PathResourceResolver());
}
// View H2
@Bean(initMethod="start", destroyMethod="stop")
public Server h2Console () throws SQLException {
return Server.createWebServer("-web","-webAllowOthers","-webDaemon","-webPort", "8082");
}
}
@@ -0,0 +1,69 @@
package com.baeldung.springsecuredsockets.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.JpaVendorAdapter;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.Database;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
import java.util.Properties;
@Configuration
@ComponentScan("com.baeldung.springsecuredsockets")
@EnableTransactionManagement
@EnableJpaRepositories("com.baeldung.springsecuredsockets.repositories")
public class DataStoreConfig {
//Configuration for embededded data store through H2
@Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2)
.setName("socketDB")
.addScript("classpath:schema.sql")
.addScript("classpath:data.sql")
.setScriptEncoding("UTF-8")
.continueOnError(true)
.ignoreFailedDrops(true)
.build();
}
@Bean
public JpaVendorAdapter jpaVendorAdapter() {
final HibernateJpaVendorAdapter bean = new HibernateJpaVendorAdapter();
bean.setDatabase(Database.H2);
bean.setGenerateDdl(true);
return bean;
}
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource) {
final LocalContainerEntityManagerFactoryBean bean = new LocalContainerEntityManagerFactoryBean();
bean.setDataSource(dataSource);
bean.setJpaVendorAdapter(jpaVendorAdapter());
bean.setPackagesToScan("com.baeldung.springsecuredsockets");
//Set properties on Hibernate
Properties properties = new Properties();
properties.setProperty("hibernate.dialect", "org.hibernate.dialect.H2Dialect");
properties.setProperty("hibernate.hbm2ddl.auto", "update");
bean.setJpaProperties(properties);
return bean;
}
@Bean
public JpaTransactionManager transactionManager(EntityManagerFactory emf) {
return new JpaTransactionManager(emf);
}
}
@@ -0,0 +1,136 @@
package com.baeldung.springsecuredsockets.config;
import com.baeldung.springsecuredsockets.security.CustomAccessDeniedHandler;
import com.baeldung.springsecuredsockets.security.CustomDaoAuthenticationProvider;
import com.baeldung.springsecuredsockets.security.CustomLoginSuccessHandler;
import com.baeldung.springsecuredsockets.security.CustomLogoutSuccessHandler;
import com.baeldung.springsecuredsockets.security.CustomUserDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
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.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
/**
* @EnableGlobalAuthentication annotates:
* @EnableWebSecurity
* @EnableWebMvcSecurity
* @EnableGlobalMethodSecurity Passing in 'prePostEnabled = true' allows:
* <p>
* Pre/Post annotations such as:
* @PreAuthorize("hasRole('ROLE_USER')")
*/
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
@EnableWebSecurity
@ComponentScan("com.baeldung.springsecuredsockets")
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private CustomUserDetailsService customUserDetailsService;
/**
* Login, Logout, Success, and Access Denied beans/handlers
*/
@Bean
public AccessDeniedHandler accessDeniedHandler() {
return new CustomAccessDeniedHandler();
}
@Bean
public LogoutSuccessHandler logoutSuccessHandler() {
return new CustomLogoutSuccessHandler();
}
@Bean
public AuthenticationSuccessHandler loginSuccessHandler() {
return new CustomLoginSuccessHandler();
}
/**
* Authentication beans
*/
@Bean
public PasswordEncoder encoder() {
return new BCryptPasswordEncoder();
}
@Bean
public DaoAuthenticationProvider authenticationProvider() {
final DaoAuthenticationProvider bean = new CustomDaoAuthenticationProvider();
bean.setUserDetailsService(customUserDetailsService);
bean.setPasswordEncoder(encoder());
return bean;
}
/**
* Order of precedence is very important.
* <p>
* Matching occurs from top to bottom - so, the topmost match succeeds first.
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/", "/index", "/authenticate")
.permitAll()
.antMatchers("/secured/**/**",
"/secured/success", "/secured/socket", "/secured/success")
.authenticated()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login").permitAll()
.usernameParameter("username")
.passwordParameter("password")
.loginProcessingUrl("/authenticate")
.successHandler(loginSuccessHandler())
.failureUrl("/denied").permitAll()
.and()
.logout()
.logoutSuccessHandler(logoutSuccessHandler())
.and()
/**
* Applies to User Roles - not to login failures or unauthenticated access attempts.
*/
.exceptionHandling()
.accessDeniedHandler(accessDeniedHandler())
.and()
.authenticationProvider(authenticationProvider());
/** Disabled for local testing */
http
.csrf().disable();
/** This is solely required to support H2 console viewing in Spring MVC with Spring Security */
http
.headers()
.frameOptions()
.disable();
}
@Override
protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider());
}
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/resources/**");
}
}
@@ -0,0 +1,27 @@
package com.baeldung.springsecuredsockets.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.AbstractWebSocketMessageBrokerConfigurer;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
@Configuration
@EnableWebSocketMessageBroker
@ComponentScan("com.baeldung.springsecuredsockets.controllers")
public class SocketBrokerConfig extends AbstractWebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/secured/history");
config.setApplicationDestinationPrefixes("/spring-security-mvc-socket");
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/secured/chat").withSockJS();
}
}
@@ -0,0 +1,21 @@
package com.baeldung.springsecuredsockets.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.messaging.MessageSecurityMetadataSourceRegistry;
import org.springframework.security.config.annotation.web.socket.AbstractSecurityWebSocketMessageBrokerConfigurer;
@Configuration
public class SocketSecurityConfig extends AbstractSecurityWebSocketMessageBrokerConfigurer {
@Override
protected boolean sameOriginDisabled() {
return true;
}
@Override
protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) {
messages
.simpDestMatchers("/secured/**").authenticated()
.anyMessage().authenticated();
}
}
@@ -0,0 +1,24 @@
package com.baeldung.springsecuredsockets.config;
import org.hibernate.cfg.Configuration;
import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;
public class WebAppInitializer implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext container) throws ServletException {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.register(AppConfig.class);
context.setServletContext(container);
ServletRegistration.Dynamic servlet = container.addServlet("dispatcher", new DispatcherServlet(context));
servlet.setLoadOnStartup(1);
servlet.addMapping("/");
}
}
@@ -0,0 +1,19 @@
package com.baeldung.springsecuredsockets.controllers;
import org.springframework.security.web.csrf.CsrfToken;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
@Controller
public class CsrfTokenController {
@RequestMapping(value = "/csrf", method = RequestMethod.GET)
public @ResponseBody
String getCsrfToken(HttpServletRequest request) {
CsrfToken csrf = (CsrfToken) request.getAttribute(CsrfToken.class.getName());
return csrf.getToken();
}
}
@@ -0,0 +1,28 @@
package com.baeldung.springsecuredsockets.controllers;
import com.baeldung.springsecuredsockets.transfer.socket.Message;
import com.baeldung.springsecuredsockets.transfer.socket.OutputMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.stereotype.Controller;
import java.text.SimpleDateFormat;
import java.util.Date;
@Controller
public class SocketController {
@Autowired
private SimpMessagingTemplate simpMessagingTemplate;
private static final Logger log = LoggerFactory.getLogger(SocketController.class);
@MessageMapping("/secured/chat")
@SendTo("/secured/history")
public OutputMessage send(Message msg) throws Exception {
OutputMessage out = new OutputMessage(msg.getFrom(), msg.getText(), new SimpleDateFormat("HH:mm").format(new Date()));
return out;
}
}
@@ -0,0 +1,51 @@
package com.baeldung.springsecuredsockets.domain;
import javax.persistence.*;
import java.util.Set;
@Entity
@Table(name = "role")
public class Role {
@Id
//Slight increase in performance over GenerationType.IDENTITY
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "role_id", updatable = false, nullable = false)
private long role_id;
@Column(name = "role", nullable = false)
private String role;
/**
* Many to Many Example - see Role.
* <p>
* One User many have many Roles.
* Each Role may be assigned to many Users.
*/
@ManyToMany(mappedBy = "roles", fetch = FetchType.EAGER)
private Set<User> users;
public long getRole_id() {
return role_id;
}
public void setRole_id(long role_id) {
this.role_id = role_id;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
public Set<User> getUsers() {
return users;
}
public void setUsers(Set<User> users) {
this.users = users;
}
}
@@ -0,0 +1,65 @@
package com.baeldung.springsecuredsockets.domain;
import javax.persistence.*;
import java.util.Set;
//Custom User Model
@Entity
@Table(name = "user")
public class User {
@Id
//Slight increase in performance over GenerationType.IDENTITY
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "user_id", updatable = false, nullable = false)
private long user_id;
@Column(name = "username", nullable = false)
private String username;
@Column(name = "password", nullable = false)
private String password;
/**
* Many to Many Example - see Role.
* <p>
* One User many have many Roles.
* Each Role may be assigned to many Users.
*/
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(name = "user_role", joinColumns = @JoinColumn(name = "user_id"), inverseJoinColumns = @JoinColumn(name = "role_id"))
private Set<Role> roles;
public long getUser_id() {
return user_id;
}
public void setUser_id(long user_id) {
this.user_id = user_id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Set<Role> getRoles() {
return roles;
}
public void setRoles(Set<Role> roles) {
this.roles = roles;
}
}
@@ -0,0 +1,11 @@
package com.baeldung.springsecuredsockets.repositories;
import com.baeldung.springsecuredsockets.domain.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
User findByUsername(String username);
}
@@ -0,0 +1,23 @@
package com.baeldung.springsecuredsockets.security;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* Applies to User Roles - not to login failures or unauthenticaed access attempts.
*/
public class CustomAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException exc) throws IOException, ServletException {
response.setStatus(HttpStatus.BAD_REQUEST.value());
response.sendRedirect(request.getContextPath() + "/denied");
}
}
@@ -0,0 +1,42 @@
package com.baeldung.springsecuredsockets.security;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
public class CustomDaoAuthenticationProvider extends DaoAuthenticationProvider {
Logger log = LoggerFactory.getLogger(CustomDaoAuthenticationProvider.class);
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String name = authentication.getName();
String password = authentication.getCredentials().toString();
UserDetails u = null;
try {
u = getUserDetailsService().loadUserByUsername(name);
} catch (UsernameNotFoundException ex) {
log.error("User '" + name + "' not found");
} catch (Exception e) {
log.error("Exception in CustomDaoAuthenticationProvider: " + e);
}
if (u != null) {
if (u.getPassword().equals(password)) {
return new UsernamePasswordAuthenticationToken(u, password, u.getAuthorities());
}
}
throw new BadCredentialsException(messages.getMessage("CustomDaoAuthenticationProvider.badCredentials", "Bad credentials"));
}
}
@@ -0,0 +1,25 @@
package com.baeldung.springsecuredsockets.security;
import com.baeldung.springsecuredsockets.domain.User;
import com.baeldung.springsecuredsockets.repositories.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class CustomLoginSuccessHandler implements AuthenticationSuccessHandler {
@Autowired
UserRepository userRepository;
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException {
User user = userRepository.findByUsername(authentication.getName());
response.setStatus(HttpStatus.OK.value());
response.sendRedirect(request.getContextPath() + "/secured/success");
}
}
@@ -0,0 +1,21 @@
package com.baeldung.springsecuredsockets.security;
import org.springframework.http.HttpStatus;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class CustomLogoutSuccessHandler implements LogoutSuccessHandler {
@Override
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
throws IOException, ServletException {
response.setStatus(HttpStatus.OK.value());
response.sendRedirect(request.getContextPath() + "/index");
}
}
@@ -0,0 +1,51 @@
package com.baeldung.springsecuredsockets.security;
import com.baeldung.springsecuredsockets.domain.Role;
import com.baeldung.springsecuredsockets.domain.User;
import com.baeldung.springsecuredsockets.repositories.UserRepository;
import com.baeldung.springsecuredsockets.transfer.user.CustomUserDetails;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import java.util.Collection;
import java.util.HashSet;
@Service()
public class CustomUserDetailsService implements UserDetailsService {
Logger log = LoggerFactory.getLogger(CustomUserDetailsService.class);
@Autowired
private UserRepository userRepository;
public CustomUserDetailsService() {
super();
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
try {
User user = userRepository.findByUsername(username);
if (user != null) return new CustomUserDetails(user, getAuthorities(user));
} catch (Exception ex) {
log.error("Exception in CustomUserDetailsService: " + ex);
}
return null;
}
private Collection<GrantedAuthority> getAuthorities(User user) {
Collection<GrantedAuthority> authorities = new HashSet<>();
for (Role role : user.getRoles()) {
GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(role.getRole());
authorities.add(grantedAuthority);
}
return authorities;
}
}
@@ -0,0 +1,12 @@
package com.baeldung.springsecuredsockets.security;
import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
/**
* This is required to enable springSecurityFilterChain.
*
* Remember that Spring Security utilizes filters to intercept and manage requests
* according to the specified authorization and authentication rules
*/
public class SecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer {}
@@ -0,0 +1,20 @@
package com.baeldung.springsecuredsockets.transfer.socket;
public class Message {
private String from;
private String text;
public String getFrom() {
return from;
}
public void setFrom(String from) {
this.from = from;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
}
@@ -0,0 +1,17 @@
package com.baeldung.springsecuredsockets.transfer.socket;
public class OutputMessage extends Message {
private String time;
public OutputMessage(final String from, final String text, final String time) {
setFrom(from);
setText(text);
this.time = time;
}
public String getTime() {
return time;
}
public void setTime(String time) { this.time = time; }
}
@@ -0,0 +1,25 @@
package com.baeldung.springsecuredsockets.transfer.user;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;
import java.util.Collection;
public class CustomUserDetails extends User {
private com.baeldung.springsecuredsockets.domain.User user;
public CustomUserDetails(com.baeldung.springsecuredsockets.domain.User user, Collection<? extends GrantedAuthority> authorities) {
super(user.getUsername(), user.getPassword(), authorities);
this.user = user;
}
public CustomUserDetails(com.baeldung.springsecuredsockets.domain.User user, boolean enabled, boolean accountNonExpired, boolean credentialsNonExpired, boolean accountNonLocked, Collection<? extends GrantedAuthority> authorities) {
super(user.getUsername(), user.getPassword(), enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities);
this.user = user;
}
public com.baeldung.springsecuredsockets.domain.User getUser() {
return user;
}
}
@@ -0,0 +1,18 @@
INSERT INTO user (user_id, username, password)
VALUES (1, 'user', 'password');
INSERT INTO user (user_id, username, password)
VALUES (2, 'admin','password');
INSERT INTO user (user_id, username, password)
VALUES (3, 'mary','password');
INSERT INTO role (role_id, role)
VALUES (1, 'USER');
INSERT INTO role (role_id, role)
VALUES (2, 'ADMIN');
INSERT INTO user_role (id, user_id, role_id)
VALUES (1, 1, 1);
INSERT INTO user_role (id, user_id, role_id)
VALUES (2, 2, 2);
INSERT INTO user_role (id, user_id, role_id)
VALUES (3, 3, 1);
@@ -0,0 +1,26 @@
DROP TABLE IF EXISTS user;
CREATE TABLE user (
user_id INT(11) NOT NULL AUTO_INCREMENT,
username VARCHAR(45) NOT NULL ,
password VARCHAR(45) NOT NULL ,
UNIQUE KEY uni_user_username (username),
PRIMARY KEY (user_id)
);
DROP TABLE IF EXISTS role;
CREATE TABLE role (
role_id INT(11) NOT NULL AUTO_INCREMENT,
role VARCHAR(45) NOT NULL,
UNIQUE KEY uni_role_role (role),
PRIMARY KEY (role_id)
);
/** JOIN TABLES */
DROP TABLE IF EXISTS user_role;
CREATE TABLE user_role(
id INT(11) NOT NULL AUTO_INCREMENT,
user_id INT(11) NOT NULL,
role_id INT(11) NOT NULL,
PRIMARY KEY (id)
);
@@ -0,0 +1,18 @@
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1" %>
<!DOCTYPE html>
<html>
<head>
<title>Spring Secured Sockets</title>
<link href="<c:url value="/resources/styles/style.css"/>" rel="stylesheet">
<script src="<c:url value="/resources/vendor/jquery/jquery.min.js" />"></script>
</head>
<body>
<h1>ACCESS DENIED!</h1>
<br/>
<br/>
<a href="${pageContext.request.contextPath}/login">Click to login.</a>
</body>
</html>
@@ -0,0 +1,30 @@
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1" %>
<!DOCTYPE html>
<html ng-app='angularApp'>
<head>
<title>Spring Secured Sockets</title>
<link href="<c:url value="/resources/styles/style.css"/>" rel="stylesheet">
<script src="<c:url value="/resources/vendor/jquery/jquery.min.js" />"></script>
<script src="<c:url value="/resources/vendor/angular/angular.min.js" />"></script>
<script src="<c:url value="/resources/vendor/angular/angular-route.min.js" />"></script>
<script src="<c:url value="/resources/scripts/app.js" />"></script>
<script src="<c:url value="/resources/scripts/services/SocketService.js" />"></script>
<script src="<c:url value="/resources/scripts/controllers/indexController.js" />"></script>
<script src="<c:url value="/resources/scripts/controllers/loginController.js" />"></script>
<script src="<c:url value="/resources/scripts/controllers/socketController.js" />"></script>
<script src="<c:url value="/resources/scripts/controllers/successController.js" />"></script>
<script src="<c:url value="/resources/scripts/routes/router.js" />"></script>
</head>
<body ng-controller="indexController">
<h1>Welcome!</h1>
<br />
<span>{{greeting}}</span>
<br />
<br />
<a href="${pageContext.request.contextPath}/login">Click to login.</a>
</body>
</html>
@@ -0,0 +1,45 @@
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1" %>
<!DOCTYPE html>
<html ng-app='angularApp'>
<head>
<title>Spring Secured Sockets</title>
<link href="<c:url value="/resources/styles/style.css"/>" rel="stylesheet">
<script src="<c:url value="/resources/vendor/jquery/jquery.min.js" />"></script>
<script src="<c:url value="/resources/vendor/angular/angular.min.js" />"></script>
<script src="<c:url value="/resources/vendor/angular/angular-route.min.js" />"></script>
<script src="<c:url value="/resources/scripts/app.js" />"></script>
<script src="<c:url value="/resources/scripts/services/SocketService.js" />"></script>
<script src="<c:url value="/resources/scripts/controllers/indexController.js" />"></script>
<script src="<c:url value="/resources/scripts/controllers/loginController.js" />"></script>
<script src="<c:url value="/resources/scripts/controllers/socketController.js" />"></script>
<script src="<c:url value="/resources/scripts/controllers/successController.js" />"></script>
<script src="<c:url value="/resources/scripts/routes/router.js" />"></script>
</head>
<body ng-controller="loginController">
<c:set var="context" scope="session" value="${pageContext.request.contextPath}"/>
<!-- Must match the endpoint specified in security config -->
<h1>JSP Login Form</h1><br/>
<form name='f' action="authenticate" method='POST'>
<table>
<tr>
<td>User:</td>
<td><input type="text" name="username"/></td>
</tr>
<tr>
<td>Password:</td>
<td><input type="password" name="password"/></td>
</tr>
<tr>
<td><input name="submit" type="submit" value="submit"/></td>
</tr>
</table>
</form>
<!-- CSRF Token -->
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
</body>
</html>
@@ -0,0 +1,51 @@
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1" %>
<!DOCTYPE html>
<html ng-app='angularApp'>
<head>
<title>Spring Secured Sockets</title>
<link href="<c:url value="/resources/styles/style.css"/>" rel="stylesheet">
<script src="<c:url value="/resources/vendor/sockjs/sockjs.min.js" />"></script>
<script src="<c:url value="/resources/vendor/stomp/stomp.min.js" />"></script>
<script src="<c:url value="/resources/vendor/jquery/jquery.min.js" />"></script>
<script src="<c:url value="/resources/vendor/angular/angular.min.js" />"></script>
<script src="<c:url value="/resources/vendor/angular/angular-route.min.js" />"></script>
<script src="<c:url value="/resources/scripts/app.js" />"></script>
<script src="<c:url value="/resources/scripts/services/SocketService.js" />"></script>
<script src="<c:url value="/resources/scripts/controllers/indexController.js" />"></script>
<script src="<c:url value="/resources/scripts/controllers/loginController.js" />"></script>
<script src="<c:url value="/resources/scripts/controllers/socketController.js" />"></script>
<script src="<c:url value="/resources/scripts/controllers/successController.js" />"></script>
<script src="<c:url value="/resources/scripts/routes/router.js" />"></script>
</head>
<body ng-controller="socketController">
<h1>Socket Chat</h1>
<c:set var = "context" scope = "session" value = "${pageContext.request.contextPath}"/>
<div>
<div>
<input type="text" id="from" placeholder="Choose a nickname"/>
</div>
<br/>
<div>
<button id="connect" ng-click="connect('${context}');">Connect</button>
<button id="subscribe" ng-click="subscribe();">Subscribe</button>
<button id="disconnect" disabled="disabled" ng-click="disconnect();">Disconnect</button>
</div>
<br/>
<div id="conversationDiv">
<input type="text" id="text" placeholder="Write a message..."/>
<button id="sendMessage" ng-click="sendMessage('${context}');">Send</button>
<p id="response"></p>
</div>
</div>
<a href="${context}/secured/success">Click to go back!</a>
<a href="${context}/">Click to start over (you will still be authenticated)!</a>
<!-- CSRF Token -->
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
</body>
</html>

Some files were not shown because too many files have changed in this diff Show More