Spring Security Method Security Interceptor not picking up authenticationManager - authentication

I'm trying to write a custom method security interceptor. However, it isn't using the authentication manager I added to the bean properties in my security context and returning null when I check to see if the authentication manager exists. Could anyone shed light on why the authentication manager bean property isn't being used? I'm using spring security 3.0.5 on WebSphere 7.0
Here's the bean containing the method interceptor
<beans:bean id="methodInterceptor"
class="bigbank.security.CustomMethodSecInterceptor">
<beans:property name="authenticationManager" ref="authenticationManager" />
<beans:property name="accessDecisionManager" ref="universalAccessDecisionManager" />
<beans:property name="securityMetadataSource" ref="tspmMethodSecurityMetaData" />
Here's my method security interceptor
public class CustomMethodSecInterceptor extends MethodSecurityInterceptor {
private static final Log logger = LogFactory
.getLog(WebSphere2SpringSecurityPropagationInterceptor.class);
private AuthenticationManager authenticationManager = null;
private AuthenticationDetailsSource authenticationDetailsSource = new WebSpherePreAuthenticatedAuthenticationDetailsSource();
private final WASUsernameAndGroupsExtractor wasHelper;
public CustomMethodSecInterceptor() {
wasHelper = new DefaultWASUsernameAndGroupsExtractor();
}
#Override
public Object invoke(MethodInvocation mi) throws Throwable {
try {
logger.debug("Performing Spring Security authentication with WebSphere credentials");
System.out.println("##going through ss authentication");
authenticateSpringSecurityWithWASCredentials();
InterceptorStatusToken token = super.beforeInvocation(mi);
logger.debug("Proceeding with method invocation");
Object result = mi.proceed();
return super.afterInvocation(token, result);
} finally {
logger.debug("Clearing Spring Security security context");
SecurityContextHolder.clearContext();
}
}
private void authenticateSpringSecurityWithWASCredentials() {
Assert.notNull(authenticationManager); // This is where the error is coming up
Assert.notNull(authenticationDetailsSource);
String userName = wasHelper.getCurrentUserName();
if (logger.isDebugEnabled()) {
logger.debug("Creating authentication request for user " + userName);
}
PreAuthenticatedAuthenticationToken authRequest = new PreAuthenticatedAuthenticationToken(
userName, "N/A");
authRequest.setDetails(authenticationDetailsSource.buildDetails(null));
if (logger.isDebugEnabled()) {
logger.debug("Authentication request for user " + userName + ": "
+ authRequest);
}
Authentication authResponse = authenticationManager
.authenticate(authRequest);
if (logger.isDebugEnabled()) {
logger.debug("Authentication response for user " + userName + ": "
+ authResponse);
}
SecurityContextHolder.getContext().setAuthentication(authResponse);
}
public void setAuthenticationManager(
AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
}
Here's the error:
Caused by: java.lang.IllegalArgumentException: An AuthenticationManager is required
at org.springframework.util.Assert.notNull(Assert.java:112)
at org.springframework.security.access.intercept.AbstractSecurityInterceptor.afterPropertiesSet(AbstractSecurityInterceptor.java:118)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1469)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1409)
... 119 more

You have overridden the setAuthenticationManager method, so when it is invoked by Spring to inject the AuthenticationManager, it doesn't set the corresponding field in AbstractSecurityInterceptor.
Since the base class contains a getter for this property, you would be best to remove the field and setter method, and just use the getter to access the authentication manager in your code.

Related

"retrieveUser returned null - a violation of the interface contract" in springboot security when authentication

I am new to springboot security and i am trying write the signup function.My approach is to save the user and then pass the data to the autheicationmanager,but the went in here and it return null and the above error occur.
token service:
public String signup(JpaUser jpaUser) {
System.out.println(jpaUserrepository.findByUsername(jpaUser.getUsername()));
if(jpaUserrepository.findByUsername(jpaUser.getUsername()).isPresent())
{return "repeated username";}
JpaUser saveuser=jpaUserrepository.save(new JpaUser(jpaUser.getUsername(),
passwordEncoder.encode(jpaUser.getPassword()),jpaUser.getEmail(),jpaUser.getRoles()));
Authentication authentication= authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(
saveuser.getUsername(),saveuser.getPassword()));
System.out.println(authentication);
String token=generateToken(authentication);
System.out.println(token);
return token;
}
securityconfig:
#Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authConfig) {
try{System.out.println(authConfig.getAuthenticationManager());
return authConfig.getAuthenticationManager();}catch(Exception exception){
System.out.println(exception);
return null;
}
}
generate token method:
public String generateToken (Authentication authentication){
Instant now=Instant.now();
String scope=authentication.getAuthorities().stream() //Stream<capture of ? extends GrantedAuthority >
.map(GrantedAuthority::getAuthority)
.collect(Collectors.joining(" "));
JwtClaimsSet claims = JwtClaimsSet.builder()
.issuer(authentication.getName())
.issuedAt(now)
.expiresAt(now.plus(1, ChronoUnit.HOURS))
.claim("scope",scope).build();
return this.encoder.encode(JwtEncoderParameters.from((claims))).getTokenValue();
}

Webflux security authorisation test with bearer token (JWT) and custom claim

I have a Spring Boot (2.3.6.RELEASE) service that is acting as a resource server, it has been implemented using Webflux, client jwts are provided by a third party identity server.
I am attempting to test the security of the endpoints using JUnit 5 and #SpringBootTest. (For the record security appears to work as required during manual testing)
I am mutating the WebTestClient to include a JWT with an appropriate claim (myClaim), however in my custom ReactiveAuthorizationManager there is no bearer token in the requests header, thus with nothing to decode or claim to validate the request fails authorisation, as it should.
My test setup is thus:
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#ActiveProfiles("test")
class ControllerTest {
#Autowired
private ApplicationContext applicationContext;
private WebTestClient webTestClient;
#BeforeEach
void init() {
webTestClient = WebTestClient
.bindToApplicationContext(applicationContext)
.apply(springSecurity())
.configureClient()
.build();
}
#Test
void willAllowAccessForJwtWithValidClaim() {
webTestClient.mutateWith(mockJwt().jwt(jwt -> jwt.claim("myClaim", "{myValue}")))
.get()
.uri("/securedEndpoint")
.exchange()
.expectStatus()
.isOk();
}
}
I have been attempting to follow this guide
I have tried the client with and without .filter(basicAuthentication()) just in case :)
It appears to me that the mockJwt() isint being put into the requests Authorization header field.
I also think that the ReactiveJwtDecoder being injected into my ReactiveAuthorizationManager will attempt to decode the test JWT against the identity provider which will fail.
I could mock the ReactiveAuthorizationManager or the ReativeJwtDecoder.
Is there anything I am missing?
Perhaps there is a way to create "test" JWTs using the Identity Services JWK set uri?
Additional detail:
Details of the ReactiveAuthorizationManager and Security Config
public class MyReactiveAuthorizationManager implements ReactiveAuthorizationManager<AuthorizationContext> {
private static final AuthorizationDecision UNAUTHORISED = new AuthorizationDecision(false);
private final ReactiveJwtDecoder jwtDecoder;
public JwtRoleReactiveAuthorizationManager(final ReactiveJwtDecoder jwtDecoder) {
this.jwtDecoder = jwtDecoder;
}
#Override
public Mono<AuthorizationDecision> check(final Mono<Authentication> authentication, final AuthorizationContext context) {
final ServerWebExchange exchange = context.getExchange();
if (null == exchange) {
return Mono.just(UNAUTHORISED);
}
final List<String> authorisationHeaders = exchange.getRequest().getHeaders().getOrEmpty(HttpHeaders.AUTHORIZATION);
if (authorisationHeaders.isEmpty()) {
return Mono.just(UNAUTHORISED);
}
final String bearer = authorisationHeaders.get(0);
return jwtDecoder.decode(bearer.replace("Bearer ", ""))
.flatMap(jwt -> determineAuthorisation(jwt.getClaimAsStringList("myClaim")));
}
private Mono<AuthorizationDecision> determineAuthorisation(final List<String> claimValues) {
if (Objects.isNull(claimValues)) {
return Mono.just(UNAUTHORISED);
} else {
return Mono.just(new AuthorizationDecision(!Collections.disjoint(claimValues, List.of("myValues")));
}
}
}
#EnableWebFluxSecurity
public class JwtSecurityConfig {
#Bean
public SecurityWebFilterChain configure(final ServerHttpSecurity http,
final ReactiveAuthorizationManager reactiveAuthorizationManager) {
http
.csrf().disable()
.logout().disable()
.authorizeExchange().pathMatchers("/securedEndpoint").access(reactiveAuthorizationManager)
.anyExchange().permitAll()
.and()
.oauth2ResourceServer()
.jwt();
return http.build();
}
}
Loosely speaking, it turns out that what I am actually doing is using a custom claim as an "Authority", that is saying "myClaim" must contain a value of "x" to allow access to a given path.
This is a little different to the claim being a simple custom claim, i.e. an additional bit of data (a users preferred colour scheme perhaps) in the token.
With that in mind I realised that the behaviour I was observing under testing was probably correct, so instead of implementing a ReactiveAuthorizationManager I choose to configure a ReactiveJwtAuthenticationConverter:
#Bean
public ReactiveJwtAuthenticationConverter jwtAuthenticationConverter() {
final JwtGrantedAuthoritiesConverter converter = new JwtGrantedAuthoritiesConverter();
converter.setAuthorityPrefix(""); // 1
converter.setAuthoritiesClaimName("myClaim");
final Converter<Jwt, Flux<GrantedAuthority>> rxConverter = new ReactiveJwtGrantedAuthoritiesConverterAdapter(converter);
final ReactiveJwtAuthenticationConverter jwtAuthenticationConverter = new ReactiveJwtAuthenticationConverter();
jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(rxConverter);
return jwtAuthenticationConverter;
}
(Comment 1; The JwtGrantedAuthoritiesConverter prepends "SCOPE_" to the claim value, this can be controlled using setAuthorityPrefix see)
This required a tweak to the SecurityWebFilterChain configuration:
http
.csrf().disable()
.logout().disable()
.authorizeExchange().pathMatchers("securedEndpoint").hasAnyAuthority("myValue)
.anyExchange().permitAll()
.and()
.oauth2ResourceServer()
.jwt(jwt -> jwt.jwtAuthenticationConverter(jwtAuthenticationConverter));
Tests
#SpringBootTest
class ControllerTest {
private WebTestClient webTestClient;
#Autowired
public void setUp(final ApplicationContext applicationContext) {
webTestClient = WebTestClient
.bindToApplicationContext(applicationContext) // 2
.apply(springSecurity()) // 3
.configureClient()
.build();
}
#Test
void myTest() {
webTestClient
.mutateWith(mockJwt().authorities(new SimpleGrantedAuthority("myValue"))) // 4
.build()
.get()
.uri("/securedEndpoint")
.exchange()
.expectStatus()
.isOk()
}
}
To make the tests "work it appears that the WebTestClient needs to bind to the application context (at comment 2).
Ideally I would have prefered to have the WebTestClient bind to the server, however the apply(springSecurity()) (at comment 3) doesnt return an appropriate type for apply when using bindToServer
There are a number of different ways to "mock" the JWT when testing, the one used (at comment 4) for alternatives see the spring docs here
I hope this helps somebody else in the future, security and OAuth2 can be confusing :)
Thanks go #Toerktumlare for pointing me in the direction of useful documentation.

Spring AOP doesn't always intercept a method

I have a user service. The service has the ability to reset the password.
#Service
public final class UserService {
private final UserMapper userMapper;
#Autowired
public UserService(final UserMapper userMapper) {
this.userMapper = userMapper;
}
#Transactional
public String restorePassword(final String loginOrEmail) throws IllegalArgumentException {
User user = userMapper.findByUsername(loginOrEmail);
if (user == null) {
user = userMapper.findByEmail(loginOrEmail);
if (user == null) throw new IllegalArgumentException("User not found");
}
final String newPassword = PasswordGenerator.generate(2, 2, 2, 4);
returnPasswordAfterRestore(newPassword, user);
//Later, the password will salt and be encrypted before entering the database.
userMapper.setPassword(newPassword, user.getUserId());
return user.getEmail();
}
public void returnPasswordAfterRestore(final String password, final User user) {
System.out.println("------------------------Method run!------------------------");
}
I need to get the generated password and send it to the user. For this I use Spring AOP.
#Before("execution(* com.example.aop.service.UserService.returnPasswordAfterRestore(..))&&args(password, user)")
public void beforeReturnPasswordAfterRestore(String password, User user) {
System.out.println("-------------------------------" + password);
System.out.println("-------------------------------" + user.getUsername() + " mail:" + user.getEmail());
}
When I make an explicit call to the returnPasswordAfterRestore () method, the aspect fulfills correctly and intercepts the parameters, this confirms the debug mode.
userService.returnPasswordAfterRestore("newPass", user);
But when I make a call to the restorePassword () method, which contains a call to the returnPasswordAfterRestore () method, the aspect does not work.
userService.restorePassword(user.getUsername());
How do I solve this problem? Or how can I get the generated password out of a method without saving it to an external variable?

Spring security custom FilterInvocationSecurityMetadataSource implementation 403 forbidden issue

To make things short I'm trying to implement a custom FilterInvocationSecurityMetadataSource in order to secure/authorize certain parts/URL endpoints dynamically in my web app using spring security 5.0.6 and Spring Boot 2.0.3.
The issue is that no matter what Role I use it always gives me the forbidden page.
I have tried several things with different role names and (believe me) I have searched the whole internet even on spring security 5.0.6 books but nothing seems to work.
This issue may be similar to this: Spring Security issue with securing URLs dynamically
Below the relevant parts of the custom FilterInvocationSecurityMetadataSource
public class DbFilterInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
public Collection<ConfigAttribute> getAttributes(Object object)
throws IllegalArgumentException {
FilterInvocation fi=(FilterInvocation)object;
String url=fi.getRequestUrl();
System.out.println("URL requested: " + url);
String[] stockArr = new String[]{"ROLE_ADMIN"};
return SecurityConfig.createList(stockArr);
}
Below the relevant parts of the custom implementation of securitywebconfigAdapter
#Configuration
public class Security extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
public <O extends FilterSecurityInterceptor> O postProcess(
O fsi) {
FilterInvocationSecurityMetadataSource newSource = new DbFilterInvocationSecurityMetadataSource();
fsi.setSecurityMetadataSource(newSource);
return fsi;
}
})
.and()
.formLogin()
.permitAll();
}
Below the relevant parts for custom userDetails authorities.
The user has the role: ROLE_ADMIN in database.
public class CustomUserDetails extends User implements UserDetails {
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
List<String> dbRoles=new ArrayList<String>();
for (Role userRole : super.getRoles()) {
dbRoles.add(userRole.getType());
}
List<SimpleGrantedAuthority> authorities=new ArrayList<SimpleGrantedAuthority>();
for (String role : dbRoles) {
authorities.add(new SimpleGrantedAuthority(role));
}
return authorities;
}
What am I doing wrong??
If more code is needed just comment below.
If you have even good books where I can learn this dynamic part of Spring security authorization comment below.
Thanks!
I managed to get into the security flow by debugging and it seems that by creating ConfigAttributes of this SecurityConfig class is the 'culprit'
return SecurityConfig.createList(stockArr);
public static List<ConfigAttribute> createList(String... attributeNames) {
Assert.notNull(attributeNames, "You must supply an array of attribute names");
List<ConfigAttribute> attributes = new ArrayList(attributeNames.length);
String[] var2 = attributeNames;
int var3 = attributeNames.length;
for(int var4 = 0; var4 < var3; ++var4) {
String attribute = var2[var4];
attributes.add(new SecurityConfig(attribute.trim()));
}
return attributes;
}
Above is the actual implementation of the method where you can see
attributes.add(new SecurityConfig(attribute.trim()));
And this always creates an instance of SecurityConfig type.
And below you can actually see where and how the decision is being made.
private WebExpressionConfigAttribute findConfigAttribute(Collection<ConfigAttribute> attributes) {
Iterator var2 = attributes.iterator();
ConfigAttribute attribute;
do {
if (!var2.hasNext()) {
return null;
}
attribute = (ConfigAttribute)var2.next();
} while(!(attribute instanceof WebExpressionConfigAttribute));
return (WebExpressionConfigAttribute)attribute;
}
So in order for it to actually return a configattribute for checking it must be of type WebExpressionConfigAttribute which is never going to be the case because of this
attributes.add(new SecurityConfig(attribute.trim()));
So the way I fixed it is to create my own accessDecisionManager the following way
public class MyAccessDecisionManager implements AccessDecisionManager {
#Override
public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes)
throws AccessDeniedException, InsufficientAuthenticationException {
if(configAttributes == null){
return ;
}
Iterator<ConfigAttribute> ite = configAttributes.iterator();
while(ite.hasNext()){
ConfigAttribute ca = ite.next();
String needRole = ((SecurityConfig)ca).getAttribute();
for(GrantedAuthority grantedAuthority : authentication.getAuthorities()){
if(needRole.trim().equals(grantedAuthority.getAuthority().trim())){
return;
}
}
}
throw new AccessDeniedException("Access is denied");
}
And registering as above now setting the accessdecisionManager with my custom one
.withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
public <O extends FilterSecurityInterceptor> O postProcess(
O fsi) {
FilterInvocationSecurityMetadataSource newSource = new DbFilterInvocationSecurityMetadataSource();
fsi.setSecurityMetadataSource(newSource);
fsi.setAccessDecisionManager(new MyAccessDecisionManager());
return fsi;
}

Service Stack - Custom authentication on one route

In my current application, I am using Service Stack with JWT's for security. Security has been implemented and works perfectly. Trouble is, I would like to secure one route differently from the others. There is a document the logged in user retrieves, I want to make sure the document they are retrieving is theirs and not someone else's. It is very sensitive data. I would like to secure it differently because something like PostMan could be used with a valid token to retrieve any document, I want to prevent this. The users id is in the token, I would like to match it against the document that is being retrieved if possible. The current security is implemented like so:
public class AppHost: AppHostBase
{
public override void Configure(Funq.Container container)
{
Plugins.Add(new AuthFeature(() => new AuthUserSession(),
new IAuthProvider[] {
new JsonWebTokenAuthProvider("myKey", "myAudience"),
}));
}
}
JsonWebTokenAuthProvider is a custom class where security was implemented, this all works perfectly. Here is the code:
public override object Authenticate(IServiceBase authService, IAuthSession session, Authenticate request)
{
// first validate the token, then get roles from session
string header = request.oauth_token;
// if no auth header, 401
if (string.IsNullOrEmpty(header))
{
throw HttpError.Unauthorized(MissingAuthHeader);
}
string[] headerData = header.Split(' ');
// if header is missing bearer portion, 401
if (!string.Equals(headerData[0], "BEARER", StringComparison.OrdinalIgnoreCase))
{
throw HttpError.Unauthorized(InvalidAuthHeader);
}
// swap - and _ with their Base64 string equivalents
string secret = SymmetricKey.Replace('-', '+').Replace('_', '/');
string token = headerData[1].Replace("\"", "");
// set current principal to the validated token principal
Thread.CurrentPrincipal = JsonWebToken.ValidateToken(token, secret, Audience, true, Issuer);
string lanId = GetLanID(Thread.CurrentPrincipal.Identity.Name);
string proxyAsLanId = request.Meta.ContainsKey(META_PROXYID) ? request.Meta[META_PROXYID] : null;
if (HttpContext.Current != null)
{
// set the current request's user the the decoded principal
HttpContext.Current.User = Thread.CurrentPrincipal;
}
// set the session's username to the logged in user
session.UserName = Thread.CurrentPrincipal.Identity.Name;
session.Roles = GetApplicableRoles(lanId, proxyAsLanId);
authService.Request.SetItem("lanID", lanId);
authService.Request.SetItem("proxyAsLanId", proxyAsLanId);
return OnAuthenticated(authService, session, null, null);
}
I looked up RequestFilterAttribute found here, but I do not think that is what I want. Ideally, if the check fails I would like to return a 401 (unauthorized) if possible.
What is the best way to do this?
If you just want to handle one route differently than you can just add the validation in your single Service, e.g:
public object Any(MyRequest dto)
{
var lanId = base.Request.GetItem("lanId");
if (!MyIsValid(lanId))
throw HttpError.Unauthorized("Custom Auth Validation failed");
}
You could do the same in a RequestFilter, e.g:
public class CustomAuthValidationAttribute : RequestFilterAttribute
{
public override void Execute(IRequest req, IResponse res, object responseDto)
{
var lanId = req.GetItem("lanId");
if (!MyIsValid(lanId))
{
res.StatusCode = (int) HttpStatusCode.Unauthorized;
res.StatusDescription = "Custom Auth Validation failed";
res.EndRequest();
}
}
}
And apply it to a single Service:
[CustomAuthValidation]
public object Any(MyRequest dto)
{
//...
}
Or a collection of Services, e.g:
[CustomAuthValidation]
public class MyAuthServices : Service
{
public object Any(MyRequest1 dto)
{
//...
}
public object Any(MyRequest2 dto)
{
//...
}
}