I'm pretty new to Spring-Boot. I have tried to block certain routes, permit some and implement authentication for them. This works so far, but somehow I want to get the user who makes the request.
My WebSecurityConfig:
#Configuration
#Order(1)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
private final JwtTokenService tokenService;
public WebSecurityConfig(JwtTokenService tokenService) {
this.tokenService = tokenService;
}
#Bean
public JwtTokenFilter tokenFilter() {
return new JwtTokenFilter();
}
#Bean
public SecurityContextHolderAwareRequestFilter securityContextHolderAwareRequestFilter() {
return new SecurityContextHolderAwareRequestFilter();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.authorizeRequests()
.antMatchers("/login", "/apply").permitAll()
.anyRequest().authenticated();
http.addFilterBefore(tokenFilter(), UsernamePasswordAuthenticationFilter.class);
http.headers().cacheControl();
}
#Autowired
public void configureAuthentication(AuthenticationManagerBuilder builder) {
builder.authenticationProvider(new AuthenticationProvider() {
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String token = (String) authentication.getCredentials();
return tokenService.decode(token).map(AuthenticatedUser::new).orElseThrow(JwtAuthenticationException::new);
}
#Override
public boolean supports(Class<?> authentication) {
return JwtAuthentication.class.equals(authentication);
}
});
}
}
The TokenFilter:
#Component
public class JwtTokenFilter extends OncePerRequestFilter {
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws IOException, ServletException {
String header = request.getHeader("Anima-Authentication-Token");
if(header != null) {
SecurityContextHolder.getContext().setAuthentication(new JwtAuthentication(header));
}
filterChain.doFilter(request, response);
}
}
The Authentications:
public class JwtAuthentication implements Authentication {
private final String token;
public JwtAuthentication(String token) {
this.token = token;
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return null;
}
#Override
public Object getCredentials() {
return token;
}
#Override
public Object getDetails() {
return null;
}
#Override
public Object getPrincipal() {
return null;
}
#Override
public boolean isAuthenticated() {
return false;
}
#Override
public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
}
#Override
public String getName() {
return null;
}
}
public class AuthenticatedUser implements Authentication {
private final AnimaUser user;
public AuthenticatedUser(AnimaUser user) {
this.user = user;
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return new ArrayList<>();
}
#Override
public Object getCredentials() {
return null;
}
#Override
public Object getDetails() {
return null;
}
#Override
public Object getPrincipal() {
return user;
}
#Override
public boolean isAuthenticated() {
return true;
}
#Override
public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
}
#Override
public String getName() {
return user.getName();
}
}
I also tried to override the Authentication in the SecurityContextHolder with the AuthorizedUser Object:
#Autowired
public void configureAuthentication(AuthenticationManagerBuilder builder) {
builder.authenticationProvider(new AuthenticationProvider() {
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String token = (String) authentication.getCredentials();
return tokenService.decode(token).map(user - > {
SecurityContextHolder.getContext().setAuthentication(new AuthenticatedUser(user));
return new AuthenticatedUser(user);
}).orElseThrow(JwtAuthenticationException::new);
}
#Override
public boolean supports(Class<?> authentication) {
return JwtAuthentication.class.equals(authentication);
}
});
}
But this hasn't worked either. I have tried to access the User with the following methods:
#GetMapping("")
#ResponseBody
public String handle() {
// This returns the JwtAuthentication, not the AuthenticatedUser
return SecurityContextHolder.getContext().getAuthentication().getCredentials().toString();
}
#GetMapping("")
#ResponseBody
public String handle(Authentication authentication) {
// authentication is null
}
#GetMapping("")
#ResponseBody
public String handle(Principle principle) {
// principle is null
}
It is because of #Component annotation on JwtTokenFilter class. Remove that and you will be good to go. You are already defining that as a #Bean in your WebSecurityConfig class. Since you have #Component on the class it is running after the AuthenticationProvider code overriding the AuthenticatedUser set in SecurityContext with JwtAuthentication
Related
I'm trying to make an authorization mechanism in spring boot. Where I'm returning the PreAuthenticatedPrincipal as a body from the request. But after using the wrapper I'm getting a body not found error.
This is the authFilter
public class AuthKeyAuthFilter extends AbstractPreAuthenticatedProcessingFilter {
#SneakyThrows
#Override
protected Object getPreAuthenticatedPrincipal(HttpServletRequest request) {
CustomHttpRequestBody wrappedRequest = new CustomHttpRequestBody((HttpServletRequest) request);
return new String(wrappedRequest.getBody());
}
#Override
protected Object getPreAuthenticatedCredentials(HttpServletRequest request) {
return "N/A";
}
}
This is the Custom Wrapper
#Getter
public class CustomHttpRequestBody extends HttpServletRequestWrapper {
private final String body;
public CustomHttpRequestBody(HttpServletRequest request) throws IOException {
super(request);
InputStream requestInputStream = request.getInputStream();
body = new String(StreamUtils.copyToByteArray(requestInputStream));
}
#Override
public ServletInputStream getInputStream() throws IOException {
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes());
ServletInputStream servletInputStream = new ServletInputStream() {
public int read() throws IOException {
return byteArrayInputStream.read();
}
#Override
public boolean isFinished() {
return byteArrayInputStream.available() == 0;
}
#Override
public boolean isReady() {
return true;
}
#Override
public void setReadListener(ReadListener listener) {
// TODO Auto-generated method stub
}
};
return servletInputStream;
}
}
This is the securityconfig
#Configuration
#EnableWebSecurity
#Order(1)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private AuthorizationServiceHandler authorizationServiceHandler;
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
AuthKeyAuthFilter filter = new AuthKeyAuthFilter();
filter.setAuthenticationManager(new AuthenticationManager() {
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String principal = (String) authentication.getPrincipal();
if (null == principal || !authorizationServiceHandler.verify(principal))
{
throw new BadCredentialsException("Authorization Error, refer to Documentation");
}
authentication.setAuthenticated(true);
return authentication;
}
});
httpSecurity.
antMatcher("/api/**").
csrf().disable().
sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).
and().addFilter(filter).authorizeRequests().anyRequest().authenticated();
}
}
Quick help appreciated,
Thanks
I'm using #InitBinder in a controller:
#InitBinder
public void binder(WebDataBinder binder) {
binder.addValidators(new CompoundValidator(new Validator[] {
new UserAccountValidator()}));
}
#Override
public UserAccountEntity login(#RequestBody UserAccountEntity userAccount,
HttpServletResponse response) throws InvalidCredentialsException, InactiveAccountException {
return userAccountService.authenticateUserAndSetResponsenHeader(
account.getUsername(), account.getPassword(), response);
}
#Override
public UserAccountEntity create(#Valid #RequestBody UserAccountEntity userAccount,
HttpServletResponse response) throws EntityExistsException, InvalidCredentialsException, InactiveAccountException {
String username = userAccount.getUsername();
String password = userAccount.getPassword();
userAccountService.saveIfNotExistsOrExpired(username, password);
return userAccountService.authenticateUserAndSetResponsenHeader(
username, password, response);
}
I want the validator to only validate the incoming userAccount for the login endpoint, and not the create method. Right now, it validates on both methods.
Update 1
Code for the CompoundValidator:
public final class CompoundValidator implements Validator {
private final Validator[] validators;
public CompoundValidator(final Validator[] validators) {
super();
this.validators=validators;
}
#Override
public boolean supports(final Class<?> clazz) {
for (Validator v : validators) {
if (v.supports(clazz)) {
return true;
}
}
return false;
}
#Override
public void validate(Object target, Errors errors) {
for (Validator v : validators) {
if (v.supports(target.getClass())) {
v.validate(target, errors);
}
}
}
}
Update 2
Some config files:
#EnableWebSecurity
public class AppSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserAccountService userAccountService;
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
// TODO re-enable csrf after dev is done
.csrf()
.disable()
// we must specify ordering for our custom filter, otherwise it
// doesn't work
.addFilterAfter(jwtAuthenticationFilter(),
UsernamePasswordAuthenticationFilter.class)
// we don't need Session, as we are using jwt instead. Sessions
// are harder to scale and manage
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
/*
* Ignores the authentication endpoints (signup and login)
*/
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/api/authentication/**").and().ignoring()
.antMatchers(HttpMethod.OPTIONS, "/**");
}
/*
* Set user details services and password encoder
*/
#Override
protected void configure(AuthenticationManagerBuilder auth)
throws Exception {
auth.userDetailsService(userAccountService).passwordEncoder(
passwordEncoder());
}
#Bean
public JwtAuthenticationFilter jwtAuthenticationFilter() {
return new JwtAuthenticationFilter();
}
/*
* By default, spring boot adds custom filters to the filter chain which
* affects all requests this should be disabled.
*/
#Bean
public FilterRegistrationBean<JwtAuthenticationFilter> rolesAuthenticationFilterRegistrationDisable(
JwtAuthenticationFilter filter) {
FilterRegistrationBean<JwtAuthenticationFilter> registration = new FilterRegistrationBean<JwtAuthenticationFilter>(
filter);
registration.setEnabled(false);
return registration;
}
}
--
#Configuration
public class AppConfig implements WebMvcConfigurer {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**")
.addResourceLocations("classpath:/public/")
.resourceChain(false).addResolver(new CustomResourceResolver());
}
private class CustomResourceResolver implements ResourceResolver {
private Resource index = new ClassPathResource("/public/index.html");
private List<String> handledExtensions = Arrays.asList("css", "png",
"svg", "jpg", "jpeg", "gif", "ico", "js");
private List<String> ignoredPaths = Arrays.asList("^api\\/.*$");
#Override
public Resource resolveResource(HttpServletRequest request,
String requestPath, List<? extends Resource> locations,
ResourceResolverChain chain) {
return resolve(requestPath, locations);
}
#Override
public String resolveUrlPath(String resourcePath,
List<? extends Resource> locations, ResourceResolverChain chain) {
Resource resolvedResource = resolve(resourcePath, locations);
if (resolvedResource == null) {
return null;
}
try {
return resolvedResource.getURL().toString();
} catch (IOException e) {
return resolvedResource.getFilename();
}
}
private Resource resolve(String requestPath,
List<? extends Resource> locations) {
if (isIgnored(requestPath)) {
return null;
}
if (isHandled(requestPath)) {
return locations
.stream()
.map(loc -> createRelative(loc, requestPath))
.filter(resource -> resource != null
&& resource.exists()).findFirst()
.orElseGet(null);
}
return index;
}
private Resource createRelative(Resource resource, String relativePath) {
try {
return resource.createRelative(relativePath);
} catch (IOException e) {
return null;
}
}
private boolean isIgnored(String path) {
return !ignoredPaths.stream().noneMatch(
rgx -> Pattern.matches(rgx, path));
}
private boolean isHandled(String path) {
String extension = StringUtils.getFilenameExtension(path);
return handledExtensions.stream().anyMatch(
ext -> ext.equals(extension));
}
}
// TODO remove this after active development of the front-end
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.exposedHeaders("Authorization", "Content-Type")
.allowedMethods("*");
}
}
In summary, user is being authenticated, but I do appear to actually have logged into the users account.
I'm currently working on implementing LDAP authentication on a project. It appears that the authentication portion of things are working in the sense that my application does accept the correct credentials. The issue I'm having is that I cant seem to access 'principal' in my jsp views. (I was able to access all of this before making the switch to LDAP). When running a trace my CustomUserDetails service is querying and pulling the correct account information. Any assistance is appreciated
This will display the proper username:
<sec:authorize access="isAuthenticated()">
<h2><sec:authentication property="name"/></h2>
</sec:authorize>
This does not (it did work before LDAP)
<sec:authorize access="isAuthenticated()">
<h2><sec:authentication property="principal.firstName"/></h2>
</sec:authorize>
Relevant Code
SecurityConfig.java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.ldap.authentication.UserDetailsServiceLdapAuthoritiesPopulator;
import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter{
#Autowired
private CustomUserDetailsService userDetailsService;
#Bean
public CustomSaltSource customSaltSource(){ return new CustomSaltSource();}
#Bean
public AuthenticationSuccessHandler myAuthenticationSuccessHandler(){
return new AuthenticationSuccessHandler();
}
#Autowired
void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.ldapAuthentication().contextSource()
.url("ldap://bar.foo.com")
.port(####)
.and()
.userDnPatterns("cn={0},cn=users,dc=ms,dc=ds,dc=foo,dc=com")
.ldapAuthoritiesPopulator(new UserDetailsServiceLdapAuthoritiesPopulator(userDetailsService));
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/skins/**", "/css/**", "/**/laggingComponents", "/assets/**").permitAll().and()
.formLogin().loginPage("/login").permitAll().defaultSuccessUrl("/", true).successHandler(myAuthenticationSuccessHandler())
.and().logout().logoutRequestMatcher(new AntPathRequestMatcher("/logout")).deleteCookies("JSESSIONID").permitAll()
.and().authorizeRequests().antMatchers("/api/**").anonymous()
.and().authorizeRequests().anyRequest().authenticated().and().rememberMe().key("KEY").userDetailsService(userDetailsService);
}
#Override
public void configure(WebSecurity web) throws Exception {
DefaultWebSecurityExpressionHandler handler = new DefaultWebSecurityExpressionHandler();
handler.setPermissionEvaluator(new PermissionEvaluator());
web.expressionHandler(handler);
web.ignoring().antMatchers( "/skins/**", "/css/**", "/api/**", "/assets/**", "/health"); //"/**/test/**"
}
}
CustomUserDetaulsService.java
import org.hibernate.Session;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import java.util.Set;
#Service
public class CustomUserDetailsService implements UserDetailsService{
#Override
public CustomUserDetails loadUserByUsername(String username) throws UsernameNotFoundException{
Session session = DBFactory.factory.openSession();
User user = (User) session.createQuery("from User where userName =:userName")
.setParameter("userName", username).uniqueResult();
if(user == null){
throw new UsernameNotFoundException("User Not Found");
}
//Needed to initialize permissions
Set<Role> roles = user.getRoles();
int i = roles.size();
for(Role role: roles){
int j = role.getPermissions().size();
}
CustomUserDetails userDetails = new CustomUserDetails(user);
session.close();
return userDetails;
}
}
If I'm not wrong,
You switched to Ldap Authorization, set url and DN patterns but still provide userDetailsService which search user in database.
You need to set UserDetailsContextMapper by implementing the interface and creating your custom one. This will map data from ldap directory context to your custom UserDetails and return it through mapUserFromContext method.
Here is an example CustomUserDetailsContextMapper:
public class CustomUserDetailsContextMapper implements UserDetailsContextMapper {
private LdapUser ldapUser = null;
private String commonName;
#Override
public UserDetails mapUserFromContext(DirContextOperations ctx, String username, Collection<? extends GrantedAuthority> authorities) {
Attributes attributes = ctx.getAttributes();
UserDetails ldapUserDetails = (UserDetails) super.mapUserFromContext(ctx,username,authorities);
try {
commonName = attributes.get("cn").get().toString();
} catch (NamingException e) {
e.printStackTrace();
}
ldapUser = new LdapUser(ldapUserDetails);
ldapUser.setCommonName(commonName);
return ldapUser;
}
#Override
public void mapUserToContext(UserDetails user, DirContextAdapter ctx) {
}
}
My custom LdapUser:
public class LdapUser implements UserDetails
{
private String commonName;
private UserDetails ldapUserDetails;
public LdapUser(LdapUserDetails ldapUserDetails) {
this.ldapUserDetails = ldapUserDetails;
}
#Override
public String getDn() {
return ldapUserDetails.getDn();
}
#Override
public void eraseCredentials() {
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return ldapUserDetails.getAuthorities();
}
#Override
public String getPassword() {
return ldapUserDetails.getPassword();
}
#Override
public String getUsername() {
return ldapUserDetails.getUsername();
}
#Override
public boolean isAccountNonExpired() {
return ldapUserDetails.isAccountNonExpired();
}
#Override
public boolean isAccountNonLocked() {
return ldapUserDetails.isAccountNonLocked();
}
#Override
public boolean isCredentialsNonExpired() {
return ldapUserDetails.isCredentialsNonExpired();
}
#Override
public boolean isEnabled() {
return ldapUserDetails.isEnabled();
}
}
Then set CustomUserDetailsContextMapper in auth configuration. This is how you will be able to get your user from authentication.getPrincipal().
I hope I correctly understand your problem and answered.
I had to test LDAP auth + different roles for devs,admins,users
Many thanks to #Yernar Arystanov
The code is not so clean but works...
public class LdapUser implements UserDetails
{
private String commonName;
private List<String> groups;
private UserDetails ldapUserDetails;
public LdapUser(LdapUserDetails ldapUserDetails) {
this.ldapUserDetails = ldapUserDetails;
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return ldapUserDetails.getAuthorities();
}
#Override
public String getPassword() {
return ldapUserDetails.getPassword();
}
#Override
public String getUsername() {
return ldapUserDetails.getUsername();
}
#Override
public boolean isAccountNonExpired() {
return ldapUserDetails.isAccountNonExpired();
}
#Override
public boolean isAccountNonLocked() {
return ldapUserDetails.isAccountNonLocked();
}
#Override
public boolean isCredentialsNonExpired() {
return ldapUserDetails.isCredentialsNonExpired();
}
#Override
public boolean isEnabled() {
return ldapUserDetails.isEnabled();
}
public String getCommonName() {
return commonName;
}
public void setCommonName(String commonName) {
this.commonName = commonName;
}
public List<String> getGroups() {
return groups;
}
public void setGroups(List<String> groups) {
this.groups = groups;
}
}
Suggestion by #Ivan Baranuk was correct (extends LdapUserDetailsMapper):
public class CustomUserDetailsContextMapper extends LdapUserDetailsMapper {
private LdapUser ldapUser = null;
private String commonName;
private List<String> groups = new LinkedList<String>();
#Override
public UserDetails mapUserFromContext(DirContextOperations ctx, String username, Collection<? extends GrantedAuthority> authorities) {
Attributes attributes = ctx.getAttributes();
UserDetails ldapUserDetails;
ldapUserDetails = (UserDetails) super.mapUserFromContext(ctx,username,authorities);
try {
commonName = attributes.get("cn").get().toString();
Arrays.stream(ctx.getObjectAttributes("memberOf"))
.iterator()
.forEachRemaining( m -> {
groups.add(m.toString());
});
} catch (NamingException | javax.naming.NamingException e) {
e.printStackTrace();
}
ldapUser = new LdapUser((LdapUserDetails) ldapUserDetails);
ldapUser.setCommonName(commonName);
ldapUser.setGroups(groups);
return ldapUser;
}
#Override
public void mapUserToContext(UserDetails user, DirContextAdapter ctx) {
}
}
then finally in SecurityConfig:
public class SecurityConfig extends WebSecurityConfigurerAdapter {
...
public void configure(AuthenticationManagerBuilder auth) throws Exception {
...
auth.ldapAuthentication()
.userSearchBase("enter your search base")
.userSearchFilter("(&(objectClass=user)(sAMAccountName={0}))")
.userDetailsContextMapper(ldapUserDetailsMapper())
.ldapAuthoritiesPopulator(ldapAuthoritiesPopulator())
.contextSource()
.url(yourProperties.getLdapUrl())
.managerDn(yourProperties.getManagerDn())
.managerPassword(yourProperties.getManagerPassword());
}
private LdapAuthoritiesPopulator ldapAuthoritiesPopulator() {
return new LdapAuthoritiesPopulator() {
#Override
public Collection<? extends GrantedAuthority> getGrantedAuthorities(DirContextOperations userData,
String username) {
LinkedList<SimpleGrantedAuthority> res = new LinkedList();
Arrays.stream(userData.getObjectAttributes("memberOf"))
.iterator()
.forEachRemaining( m -> {
if(m.toString().equals("your_dev_group"))
res.add(new SimpleGrantedAuthority("DEV_USER"));
if(m.toString().equals("your_admin_group"))
res.add(new SimpleGrantedAuthority("ADMIN_USER"));
});
if(res.isEmpty())
return Arrays.asList(new SimpleGrantedAuthority("USER"));
else
return res;
}
};
}
#Bean
#Override
protected UserDetailsService userDetailsService() {
return super.userDetailsService();
}
#Bean
protected LdapUserDetailsMapper ldapUserDetailsMapper() {return new CustomUserDetailsContextMapper();}
}
At the end - some simple get method(authorized users only):
#RequestMapping("/logger")
public String testLogger(Authentication authentication) {
if (authentication.getAuthorities().contains(new SimpleGrantedAuthority("ROLE_DEV_USER"))) {
LdapUser userDetails = (LdapUser) authentication.getPrincipal();
log.info("WELCOME :" + userDetails.getCommonName());
userDetails.getGroups().iterator().forEachRemaining((g) - > log.info("group: " + g.toString()));
}
return ""
}
I have a spring boot application i have many endpoint in this application. When i am hitting endpoint simultaneously JSON response from two different endpoint got interchanged.
For example:
i am hitting /currency/list endpoint and /fee endpoint and i am getting /fee endpoint data in currency/list endpoint and vice versa.
I have no idea why this happening. If anybody can suggest why happening will be helpful.
Also I am using spring security token based auth in this project
CurrencyController.java
#RestController
#RequestMapping(value = UrlConstant.BASE_ADMIN_URI_V1)
#Api(value = "Admin Controller")
#Scope("request")
public class CurrencyController {
public static final Logger logger = LoggerFactory.getLogger(CurrencyController.class);
#Autowired
private LocaleService localService;
#RequestMapping(value = UrlConstant.CURRENCY_LIST_FOR_MARKET, method = RequestMethod.GET)
public ResponseEntity<Object> getCurrencyListForMarket() {
List<Currency> currencyList = currencyService.getCurrencyListForMarket();
ObjectMapper mapper = new ObjectMapper();
try {
String stringList = mapper.writeValueAsString(currencyList);
logger.debug("all currency list as String: {}", stringList);
} catch (JsonProcessingException e) {
logger.debug("error in currency list: {}", e.getMessage());
e.printStackTrace();
}
return ResponseHandler.response(HttpStatus.OK, false, localService.getMessage("currency.list.success"),
currencyList);
}
}
AdminController.java
#RestController
#RequestMapping(value = UrlConstant.BASE_ADMIN_URI_V1)
#Api(value = "Admin Controller")
#Scope("request")
public class AdminController {
#Autowired
private LocaleService localeService;
#Autowired
private FeeService feeService;
#RequestMapping(value = UrlConstant.TRADING_FEES, method = RequestMethod.GET)
public ResponseEntity<Object> getTradingFees() {
TradingFee fee = tradingFeeService.getTradingFee();
return ResponseHandler.response(HttpStatus.OK, true,
localeService.getMessage("admin.transaction.fees.found.success"), fee);
}
}
TokenAuthenticationFilter.java
public class TokenAuthenticationFilter extends GenericFilterBean {
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
final HttpServletRequest httpRequest = (HttpServletRequest) request;
// extract token from header
String token = httpRequest.getHeader("Authorization");
if (token != null && !token.isEmpty()) {
AuthenticationTokenRepo authenticationTokenRepository = WebApplicationContextUtils
.getRequiredWebApplicationContext(httpRequest.getServletContext())
.getBean(AuthenticationTokenRepo.class);
// check whether token is valid
AuthenticationToken authToken = authenticationTokenRepository.findByToken(token);
if (authToken != null) {
// Add user to SecurityContextHolder
final UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
authToken.getUser(), null, new ApplicationUserDetail(authToken.getUser()).getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
chain.doFilter(request, response);
SecurityContextHolder.clearContext();
}
SecurityConfig.java
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private Environment environment;
#Override
protected void configure(final HttpSecurity http) throws Exception {
http.csrf().disable().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.authorizeRequests().antMatchers(HttpMethod.POST, "/api/v1/login").permitAll()
.antMatchers(HttpMethod.POST, "/api/v1/user/register").permitAll().anyRequest().authenticated();
// Implementing Token based authentication in this filter
final TokenAuthenticationFilter tokenFilter = new TokenAuthenticationFilter();
http.addFilterBefore(tokenFilter, BasicAuthenticationFilter.class);
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers(UrlConstant.BASE_ADMIN_URI_V1 + UrlConstant.CURRENCY_LIST_FOR_MARKET);
web.ignoring().antMatchers(UrlConstant.BASE_ADMIN_URI_V1 + UrlConstant.TRADING_FEES);
}
}
}
ApplicationUserDetail.java
public class ApplicationUserDetail implements UserDetails,Serializable {
private static final long serialVersionUID = 1L;
transient User user;
public ApplicationUserDetail(User user) {
this.user = user;
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
AuthorityUtils.commaSeparatedStringToAuthorityList(user.getRole().getName());
}
#Override
public String getPassword() {
return user.getPassword();
}
#Override
public String getUsername() {
return user.getEmailId();
}
#Override
public boolean isAccountNonExpired() {
return true;
}
#Override
public boolean isAccountNonLocked() {
return true;
}
#Override
public boolean isCredentialsNonExpired() {
return true;
}
#Override
public boolean isEnabled() {
return user.getIsEnabled();
}
}
More info: When i am printing response in my api it is correct but when i am printing it in my Authentication filter i got the response of fee api in my currency api so i think there is some problem between api to filter.
I want to implement my own authentication with spring. To keep things simple at first I'm going to implement the first step without any session, but an HTTP-Authorization header sent in every request.
I've read the documentation, many tutorials and of course searched on stackoverflow, but I couldn't fix it.
What I have is a filter (RequestFilter), which extracts the Authorization header and initializes the security context with an own Authentication (AuthenticationToken). Then there is an AuthenticationProvider (TokenAuthenticationProvider) supporting my AuthenticationToken. The filter is working, the authentication provider is not. None of the methods in TokenAuthenticationProvider are invoked. Can you help me, thanks :).
#EnableWebSecurity
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private static final Logger LOGGER = Logger.getLogger(SecurityConfig.class.getName());
#Autowired
TokenAuthenticationProvider tokenAuthenticationProvider;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.addFilterBefore(new RequestFilter(), BasicAuthenticationFilter.class)
.csrf().disable()
;
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.authenticationProvider(tokenAuthenticationProvider)
;
}
}
#Component
public class TokenAuthenticationProvider implements AuthenticationProvider {
private static final Logger LOGGER = Logger.getLogger(TokenAuthenticationProvider.class.getName());
#Autowired
ClientRepository clientRepository;
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
final String authToken = authentication.getCredentials().toString();
LOGGER.severe("AUTH TOKEN: " + authToken);
return Optional.ofNullable(clientRepository.findByAuthToken(authToken))
.map((Client client) -> new AuthenticationToken(client.getId(), client.getAuthToken()))
.orElseThrow(() -> new AccessDeniedException(""));
}
#Override
public boolean supports(Class<?> authentication) {
LOGGER.severe(authentication.getName());
return AuthenticationToken.class.isAssignableFrom(authentication);
}
}
public class RequestFilter extends OncePerRequestFilter {
private final Logger LOGGER = Logger.getLogger(RequestFilter.class.getName());
#Override
public void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
LOGGER.severe("RequestFilter works");
Optional.ofNullable(request.getHeader("Authorization"))
.ifPresent((String token) -> SecurityContextHolder
.getContext()
.setAuthentication(new AuthenticationToken(token))
);
chain.doFilter(request, response);
}
}
public class AuthenticationToken extends AbstractAuthenticationToken {
private final String credentials;
private final Long principal;
public AuthenticationToken(String credentials) {
this(null, credentials);
setAuthenticated(false);
}
public AuthenticationToken(Long principal, String credentials) {
super(null);
this.principal = principal;
this.credentials = credentials;
setAuthenticated(true);
}
#Override
public Object getCredentials() {
return credentials;
}
#Override
public Object getPrincipal() {
return principal;
}
}
#RestController
public class HttpGateController {
private static final Logger LOGGER = Logger.getLogger(HttpGateController.class.getName());
#RequestMapping(
name="/gate",
method= RequestMethod.POST,
produces = MediaType.APPLICATION_JSON_UTF8_VALUE
)
public String gateAction(#RequestBody String request) {
Optional.ofNullable(SecurityContextHolder.getContext())
.map(SecurityContext::getAuthentication)
.map(Authentication::getCredentials)
.map(ObjectUtils::nullSafeToString)
.ifPresent(LOGGER::severe);
return request;
}
#RequestMapping("/status")
public String statusAction() {
return "It works.";
}
}