Skip to content
This repository was archived by the owner on Apr 16, 2023. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,4 @@ hs_err_pid*
build/
.gradle/
classes/
out/
8 changes: 8 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,16 @@ configurations {
dependencies {
compile('org.springframework.boot:spring-boot-starter-thymeleaf')
compile('org.springframework.boot:spring-boot-starter-web')
compile('net.sourceforge.nekohtml:nekohtml:1.9.21')
compile('org.springframework.boot:spring-boot-starter-data-mongodb')
compile("de.flapdoodle.embed:de.flapdoodle.embed.mongo")
compile group: 'org.springframework.boot', name: 'spring-boot-starter-security'
compile group: 'io.jsonwebtoken', name: 'jjwt', version: '0.7.0'
compile group: 'io.springfox', name: 'springfox-swagger2', version: '2.6.1'
compile group: 'org.springframework', name: 'spring-aspects'

compileOnly 'org.projectlombok:lombok:1.16.18'

compile group: 'com.rabbitmq', name: 'amqp-client', version: '5.0.0'

runtime('com.h2database:h2')
Expand Down
11 changes: 11 additions & 0 deletions src/main/java/ro/ubb/istudent/aspects/Loggable.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package ro.ubb.istudent.aspects;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(value = ElementType.METHOD)
public @interface Loggable {
}
30 changes: 30 additions & 0 deletions src/main/java/ro/ubb/istudent/aspects/LogginAspect.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package ro.ubb.istudent.aspects;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;

import java.util.Arrays;

@Aspect
@Component
public class LogginAspect {

private Logger logger = LoggerFactory.getLogger(LogginAspect.class);

@Before("execution(public * *(..)) && @annotation(ro.ubb.istudent.aspects.Loggable)")
public void logBefore(JoinPoint joinPoint) throws Throwable {
String userName = SecurityContextHolder.getContext().getAuthentication().getName();

String logInfo = "user: " + userName + " called method: " + joinPoint.getSignature().getName() +
"with params: " + Arrays.toString(joinPoint.getArgs());

logger.info(logInfo);

}

}
57 changes: 57 additions & 0 deletions src/main/java/ro/ubb/istudent/config/CorsFilter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package ro.ubb.istudent.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.servlet.*;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Component
public class CorsFilter implements Filter {

private final SecurityTokenProperties tokenProperties;

@Autowired
public CorsFilter(SecurityTokenProperties tokenProperties) {
this.tokenProperties = tokenProperties;
}

public void init(FilterConfig filterConfig) {
}

/**
* The <code>doFilter</code> method of the Filter is called by the container
* each time a request/response pair is passed through the chain due to a
* client request for a resource at the end of the chain. The FilterChain
* passed in to this method allows the Filter to pass on the request and
* response to the next entity in the chain.
* <p>
* Directly set headers on the response after invocation of the next
* entity in the filter chain.
*
* @param req The request to process
* @param res The response associated with the request
* @param chain Provides access to the next filter in the chain for this
* filter to pass the request and response to for further
* processing
* @throws IOException if an I/O error occurs during this filter's
* processing of the request
* @throws ServletException if the processing fails for any other reason
*/
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) res;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, PUT, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept , Cache-Control , " +
tokenProperties.getHeader());
chain.doFilter(req, res);
}

public void destroy() {
}

}
20 changes: 20 additions & 0 deletions src/main/java/ro/ubb/istudent/config/SecurityTokenProperties.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package ro.ubb.istudent.config;

import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
@Data
public class SecurityTokenProperties {

@Value("${security.token.secretKey}")
private String secretKey;

@Value("${security.token.header}")
private String header;

@Value("${security.token.expiration}")
private Long expiration;

}
33 changes: 33 additions & 0 deletions src/main/java/ro/ubb/istudent/config/SwaggerConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package ro.ubb.istudent.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

@Configuration
@EnableSwagger2
public class SwaggerConfig {

@Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("ro.ubb.istudent.controller"))
.build();
}

private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("Rest info")
.description("Information about rest endpoints")
.version("1.0")
.build();
}

}
122 changes: 122 additions & 0 deletions src/main/java/ro/ubb/istudent/config/WebSecurityConfiguration.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
package ro.ubb.istudent.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.data.mongodb.config.EnableMongoAuditing;
import org.springframework.http.HttpMethod;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.security.authentication.AuthenticationManager;
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.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.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.access.channel.ChannelProcessingFilter;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import ro.ubb.istudent.security.AuthenticationTokenFilter;
import ro.ubb.istudent.security.EntryPointUnauthorizedHandler;

@EnableWebSecurity
@EnableScheduling
@Configuration
@Order(1)
@EnableGlobalMethodSecurity(prePostEnabled = true)
@EnableMongoAuditing
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {

private final CorsFilter corsFilter;
private final UserDetailsService userDetailsService;
private final EntryPointUnauthorizedHandler unauthorizedHandler;

@Autowired
public WebSecurityConfiguration(CorsFilter corsFilter, UserDetailsService userDetailsService, EntryPointUnauthorizedHandler unauthorizedHandler) {
this.corsFilter = corsFilter;
this.userDetailsService = userDetailsService;
this.unauthorizedHandler = unauthorizedHandler;
}

public WebSecurityConfiguration(boolean disableDefaults, CorsFilter corsFilter, UserDetailsService userDetailsService, EntryPointUnauthorizedHandler unauthorizedHandler) {
super(disableDefaults);
this.corsFilter = corsFilter;
this.userDetailsService = userDetailsService;
this.unauthorizedHandler = unauthorizedHandler;
}


@Autowired
public void configureAuthentication(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
authenticationManagerBuilder
.userDetailsService(this.userDetailsService)
.passwordEncoder(passwordEncoder());
}

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

@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}

@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.addFilterBefore(corsFilter, ChannelProcessingFilter.class)
.csrf()
.disable()
.exceptionHandling()
.authenticationEntryPoint(this.unauthorizedHandler)
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
// note: you can add as well js, css, png etc files if needed
// just follow the same pattern changing the file extension
// for static file serving check also the application.properties file
// you should indicate the path to resources -> static-location
.regexMatchers("/").permitAll()
.regexMatchers("/.*.html").permitAll()
.regexMatchers("/.*.js").permitAll()
.regexMatchers("/.*.css").permitAll()
.regexMatchers("/.*.png").permitAll()
.regexMatchers("/.*.jpg").permitAll()
.regexMatchers("/.*.jpeg").permitAll()
.antMatchers("/login").permitAll()
.antMatchers("/user/save").permitAll()
.antMatchers("/v2/api-docs").permitAll()
.antMatchers(HttpMethod.OPTIONS).permitAll()
.anyRequest().authenticated();
httpSecurity
.addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);
}

@Bean
public AuthenticationTokenFilter authenticationTokenFilterBean() throws Exception {
AuthenticationTokenFilter authenticationTokenFilter = new AuthenticationTokenFilter();
authenticationTokenFilter.setAuthenticationManager(authenticationManager());
return authenticationTokenFilter;
}

@Configuration
public class CustomWebMvcConfigurerAdapter extends WebMvcConfigurerAdapter {

@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("forward: public/index.html");
super.addViewControllers(registry);
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package ro.ubb.istudent.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import ro.ubb.istudent.aspects.Loggable;
import ro.ubb.istudent.domain.ValidToken;
import ro.ubb.istudent.dto.AuthenticationRequest;
import ro.ubb.istudent.dto.AuthenticationResponse;
import ro.ubb.istudent.repository.ValidTokenRepository;
import ro.ubb.istudent.security.TokenUtils;
import ro.ubb.istudent.util.HttpHeaders;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;

@RestController
public class AuthenticationController {

private final AuthenticationManager authenticationManager;
private final TokenUtils tokenUtils;
private final UserDetailsService userDetailsService;
private final ValidTokenRepository validTokenRepository;

@Autowired
public AuthenticationController(TokenUtils tokenUtils, AuthenticationManager authenticationManager, UserDetailsService userDetailsService, ValidTokenRepository validTokenRepository) {
this.tokenUtils = tokenUtils;
this.authenticationManager = authenticationManager;
this.userDetailsService = userDetailsService;
this.validTokenRepository = validTokenRepository;
}

@Loggable
@RequestMapping(path = "/login", method = RequestMethod.POST)
public ResponseEntity<?> authenticationRequest(@RequestBody AuthenticationRequest authenticationRequest)
throws AuthenticationException {

// Perform the authentication
String userNameOrEmail = authenticationRequest.getEmail() != null ? authenticationRequest.getEmail() : authenticationRequest.getUserName();

Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
userNameOrEmail,
authenticationRequest.getPassword()
));

SecurityContextHolder.getContext().setAuthentication(authentication);

// Reload password post-authentication so we can generate token
UserDetails userDetails = userDetailsService.loadUserByUsername(userNameOrEmail);
String token = tokenUtils.generateToken(userDetails);
Date expirationDate = tokenUtils.getExpirationDateFromToken(token);

validTokenRepository.save(ValidToken.builder()
.token(token)
.expirationDate(expirationDate)
.build());

return ResponseEntity.ok(new AuthenticationResponse(token));
}

@RequestMapping(path = "/logoutMe", method = RequestMethod.GET)
@Transactional
public ResponseEntity<?> logout(HttpServletRequest httpRequest, HttpServletResponse response) {
String authToken = httpRequest.getHeader(HttpHeaders.AUTH_TOKEN);

Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

UserDetails userDetails =
(UserDetails) authentication.getPrincipal();

if (tokenUtils.validateToken(authToken, userDetails)) {
new SecurityContextLogoutHandler().logout(httpRequest, response, authentication);
validTokenRepository.deleteByToken(authToken);
return ResponseEntity.ok("done");
} else {
return ResponseEntity.ok("bad");
}

}

}
Loading