If I understand good, I have custom Authentication Manager class, and in there I check if someone in api pass correct credentials, but I wonder why it didn't throw exception while I passed empty username and password.
#Component
public class AuthManager implements AuthenticationManager {
private final DetailsService detailsService;
private final Logger logger = LoggerFactory.getLogger(AuthManager.class);
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
logger.info("credentials: " + authentication.getCredentials());
logger.info("principals: " + authentication.getPrincipal());
if (authentication.getCredentials() == null || authentication.getPrincipal() == null) {
throw new BadCredentialsException("Credentials are wrong");
}
UserDetails user = loadUser(authentication);
return new UsernamePasswordAuthenticationToken(user.getUsername(), null, user.getAuthorities());
}
private UserDetails loadUser(Authentication auth) {
return detailsService.loadUserByUsername(auth.getPrincipal().toString());
}
That's filter
#Component
public class UsernamePasswordJsonFilter extends UsernamePasswordAuthenticationFilter {
private final ObjectMapper objectMapper;
private final Logger logger = LoggerFactory.getLogger(this.getClass());
public UsernamePasswordJsonFilter(ObjectMapper objectMapper, AuthManager manager,
AuthSuccessHandler success, AuthFailureHandler failure) {
this.objectMapper = objectMapper;
setAuthenticationSuccessHandler(success);
setAuthenticationFailureHandler(failure);
setAuthenticationManager(manager);
setFilterProcessesUrl("/login");
}
#Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
try {
LoginDTO authenticationRequest = objectMapper.readValue(request.getInputStream(), LoginDTO.class);
Authentication auth = new UsernamePasswordAuthenticationToken(authenticationRequest.getUsername(),
authenticationRequest.getPassword());
logger.info("UsernamePasswordJsonFilter");
return getAuthenticationManager().authenticate(auth);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
If I pass correct username and password it works, but I just wonder why it didn't throw exception when credentials are empty, also there is no exception thrown in console, in case someone asks for it
When you use
LoginDTO authenticationRequest = objectMapper.readValue(request.getInputStream(), LoginDTO.class);
It return LoginDTO with login = "" and password = "". It is not a null.
"" is not the same as null. If u want to be null in LoginDTO, request should not have username or password field at all
if (authentication.getCredentials() == null || authentication.getPrincipal() == null)
Here you also to check to empty string. Let's say "".equals(authentication.getCredentials()) or authentication.getCredentials().isEmpty()
same with authentication.getPrincipal()
Related
I have gone through the similar questions asked here, but none seems to tackle the problem I am facing right now.
I have actually implemented two different processes of jwt for spring boot, yet keep facing the same problem.
Here is the AuthenticationFilter that filters the request and performs the authentication. I have added log functions to narrate what part of the code does not get called. I have implemented a different approach and it still never gets called, and funnily enough, it gets called elsewhere, not just in the class that handles the authentication or the one called by the one that handles the authentication. I use Java 8, spring boot 2.6.5.
public class AuthTokenFilter extends OncePerRequestFilter {
#Autowired JwtUtils jwtUtils;
#Autowired
MyUserDetailsService userDetailsService;
private static final Logger log = LoggerFactory.getLogger(AuthController.class);
#Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
try {
String jwt = parseJwt(request);
if (jwt != null && jwtUtils.validateJwtToken(jwt)) {
log.info(jwt + " first");
String username = jwtUtils.getUsernameFromJwtToken(jwt);
log.info(username + " second");
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
log.info(userDetails + " third");
UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(userDetails, userDetails.getAuthorities());
log.info(authToken + " fourth");
authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authToken);
}
} catch (Exception e) {
throw new IllegalArgumentException("Please log in with your correct details.");
}
filterChain.doFilter(request, response);
}
private String parseJwt(HttpServletRequest request) {
String headerAuth = request.getHeader("Authorization");
if (StringUtils.hasText(headerAuth) && headerAuth.startsWith("Bearer ")) {
return headerAuth.substring(7);
}
return null;
}
}
the log with "first" and the one with second get printed out, but the "third" doesn't. This is because UserDetails userDetails = userDetailsService.loadUserByUsername(username); doesn't get called and the code doesn't throw an error. It just doesn't get called.
Here is the JwtUtils class
#Component
public class JwtUtils {
private static final Logger log = LoggerFactory.getLogger(AuthController.class);
#Value("${jwt.secret:testing123}")
String secret;
#Value("${jwt.length:3600000}")
long duration = 3600000;
public String generateJwtToken(Authentication auth) {
UserDetails userDetails = (UserDetails) auth.getPrincipal();
return Jwts.builder().setSubject(userDetails.getUsername())
.setIssuedAt(new Date()).signWith(SignatureAlgorithm.HS512, secret)
.setExpiration(new Date((new Date()).getTime() + duration))
.compact();
}
public String getUsernameFromJwtToken(String token) {
return Jwts.parser().setSigningKey(secret).parseClaimsJws(token)
.getBody().getSubject();
}
public boolean validateJwtToken(String authToken) {
try {
Jwts.parser().setSigningKey(secret).parseClaimsJws(authToken);
return true;
} catch (SignatureException e) {
throw new IllegalArgumentException("Invalid JWT signature: {}");
}
}
Here is my CustomUserDetailsService
#Service
#RequiredArgsConstructor
public class MyUserDetailsService implements UserDetailsService {
private static final Logger log = LoggerFactory.getLogger(MyUserDetailsService.class);
#Autowired
EmployeeRepository employeeRepo;
String prefixRole = "ROLE_";
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Employee employee = employeeRepo.findByUsername(username);
if (employee == null) {
throw new UsernameNotFoundException("No such employee, bro.");
}
try {
return User.withUsername(employee.getUsername()).password(employee.getPassword())
.authorities(getAllRoles(employee.getRoles())).accountExpired(false)
.accountLocked(false).credentialsExpired(false).disabled(false).build();
} catch (RuntimeException e) {
throw new IllegalArgumentException("Something bad happened and I don't know what it is.");
}
}
private List<GrantedAuthority> getAllRoles(Collection<Role> roles) {
List<GrantedAuthority> collection = new ArrayList<>();
for (Role role: roles) {
collection.add(new SimpleGrantedAuthority(prefixRole + role.getName()));
}
return collection;
}
}
The entryPoint
#Component
public class AuthEntryPointJwt implements AuthenticationEntryPoint {
#Override public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException exception) throws IOException, ServletException {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
}
}
What could be the problem?
The User login is working well but I want to add a Customer Module to the project. I know that I need to write a custom UserDetails class to get the customer Username but I want to ask if I need to write another Custom JWT filter for the Customer Login validation. Presently this is the Filter class that I have for User Login. I have added a username and password field to the Customer entity.
#Component
public class JwtRequestFilter extends OncePerRequestFilter {
#Autowired
private JwtTokenUtil jwtTokenUtil;
#Autowired
private UserAccountService myUserDetailsService;
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
final String requestTokenHeader = request.getHeader("Authorization");
String username = null;
String jwtToken = null;
if (requestTokenHeader != null) {
jwtToken = requestTokenHeader.substring(7);
try {
username = jwtTokenUtil.getUsernameFromToken(jwtToken);
} catch (IllegalArgumentException e) {
System.out.println("Unable to get JWT Token");
} catch (ExpiredJwtException e) {
System.out.println("JWT Token has expired");
}
}
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = this.myUserDetailsService.loadUserByUsername(username);
if (jwtTokenUtil.validateToken(jwtToken, userDetails)) {
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
String authorities = userDetails.getAuthorities().stream().map(GrantedAuthority::getAuthority)
.collect(Collectors.joining());
System.out.println("Authorities granted : " + authorities);
usernamePasswordAuthenticationToken
.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
}
else {
System.out.println("Not Valid Token");
}
}
chain.doFilter(request, response);
}
}
As you can see the Filter is using the custom UserDetails to verify the username . How do I add the Customer userdetails service to the filter ? This is my first multiple login project please be lenient with me.
Differentiate between user and customer while logging. Accordingly, call the different service to get user details. More can be found here.
Spring Security user authentication against customers and employee
How do I add the Customer userdetails service to the filter?: inject it as you did with UserAccountService. If you do this way, you're using 1 filter (and of course, this filter is in 1 SecurityFilterChain), you could basically implement your filter like: trying to validate your user by myUserDetailsService and if it's not successful, continue with myCustomerDetailsService.
For multiple login project. The second way you could do is using 2 SecurityFilterChain. UserJwtFilter for 1 SecurityFilterChain and CustomJwtFilter for 1 SecurityFilterChain for example. People usually do this way for different login mechanisms Basic, OAuth2, SAML2. E.g:
Basic Authentication:
#Configuration
#Order(2)
public class BasicAuthenticationFilterChain extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.requestMatchers()
.antMatchers("/login", "/logout")
.and()
OAuth2 Authentication:
#Configuration
#Order(3)
public class OAuth2AuthenticationFilterChain extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.requestMatchers()
.antMatchers("/oauth")
.and()
In this case when a request with "/login" it'll be directed to BasicAuthenticationFilterChain, and a request with "/oauth" will go to OAuth2AuthenticationFilterChain. About Order: the lower is the higher priority and once the request's processed with a SecurityFilterChain, it won't go to another SecurityFilterChain. You can implement your project this way.
Conclusion: There are a lot of ways you can implement your idea with spring security, it depends on your choice.
it looks to me like you already did.
#Autowired
private UserAccountService myUserDetailsService;
But I would suggest using a Constructor instead of #Autowired. Spring will fill in the constructor parameters just the same. This could be very slim when you use the lombok library as well.
Using a constructor also makes mocking this a bit easier for testing.
Updated as discussed in the comments:
#Log //another lombok thing
#RequiredArgsConstructor
#Component
public class JwtRequestFilter extends Filter{
private final JwtTokenUtil jwtTokenUtil;
private final UserAccountService myUserDetailsService;
private final CustomerAccountService myCustomerDetailsService;
private static final String AUTH_HEADER = "authorization";
#Override
protected void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws ServletException, IOException {
String tokenHeader = ((HttpServletRequest) request).getHeader(AUTH_HEADER);
if(hasValue(tokenHeader) && tokenHeader.toLowerCase().startsWith("bearer ")){
jwtToken = requestTokenHeader.substring(7);
String username;
String jwtToken;
try {
username = jwtTokenUtil.getUsernameFromToken(jwtToken);
if (uSecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = myUserDetailsService.loadUserByUsername(username);
if(isNull(userDetails)){
userDetails = myCustomerDetailsService.loadCustomerByUsername(username);
}
if (jwtTokenUtil.validateToken(jwtToken, userDetails)) {
var token = createSecurityToken(userDetails);
SecurityContextHolder.getContext().setAuthentication(token);
} else {
throw new RuntimeException("Not a Valid Token.");
}
} else {
log.info("Authorization already present");
}
} catch (IllegalArgumentException e) {
throw new("Unable to get JWT Token",e);
} catch (ExpiredJwtException e) {
throw new("JWT Token has expired",e);
}
} else {
throw new RuntimeException("No valid authorization header found.");
}
chain.doFilter(request, response);
}
private UsernamePasswordAuthenticationToken createSecurityToken(UserDetails userDetails){
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
log.info("Authorities granted : {}", userDetails.getAuthorities());
token.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
return token;
}
}
I have prepared configuration for Spring security for API calls. It should verify JWT token provided in the request.
http.csrf().disable().authorizeRequests()
.antMatchers("/v2/api/**/*").authenticated().and()
.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class)
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
and my controller method
#PreAuthorize("hasAuthority('ROLE_USER')")
#PutMapping(value = "/v2/api/dashboard/projects")
public List<Projects> getProjects(Principal principal) {
return dashboardService.getProjects();
}
and by executing request I get
Resolved
[org.springframework.web.HttpRequestMethodNotSupportedException:
Request method 'PUT' not supported]
When I change it to GetMapping request is being handled properly.
After settings logs from logging.level.org.springframework.web=DEBUG I can see that PUT not supported is not returned from /v2/api/dashboard/projects but from '/forbidden' which for obvious reasons doesn't support such methods.
Further investigation with debuging jwtRequestFilter showed that filter is not even executed on PUT, PATCH or DELETE methods.
code of it:
#Component
public class JwtRequestFilter extends OncePerRequestFilter {
private static final Logger log = LoggerFactory.getLogger(JwtRequestFilter.class);
#Autowired
private JwtUserDetailsService jwtUserDetailsService;
#Autowired
private JwtUtils jwtTokenUtil;
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
String requestTokenHeader = "";
try{
requestTokenHeader = WebUtils.getCookie(request, "token").getValue();
} catch (NullPointerException ex ){}
String username = null;
String jwtToken = null;
if (requestTokenHeader != null && requestTokenHeader.contains(".")) {
jwtToken = requestTokenHeader;
try {
username = jwtTokenUtil.getUsernameFromToken(jwtToken);
} catch (IllegalArgumentException e) {
log.error("Unable to get JWT Token");
} catch (ExpiredJwtException e) {
log.error("JWT Token has expired");
}
} else {
logger.warn("JWT Token does not look like token");
}
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = this.jwtUserDetailsService.loadUserByUsername(username);
if (jwtTokenUtil.validateToken(jwtToken, userDetails)) {
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
usernamePasswordAuthenticationToken
.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
}
}
chain.doFilter(request, response);
}
}
Can anyone give me a hint how to make it works so PUT and PATCH methods would be filtered with given class?
I have a JWTAuthFilter that extends OncePerRequestFilter where I am validating the token.
The validateToken method throws custom exceptions(CredentialsChangedException and TooManyDevicesException which extend org.springframework.security.core.AuthenticationException)
These exceptions are caught in the filter properly but when they move forward to the AuthenticationEntryPoint, the AuthenticationException turns into an instanceof InsufficientAuthenticationException and the custom error message that I want to return as a response is lost.
#Component
public class JwtAuthFilter extends OncePerRequestFilter {
private static final String BEARER = "Bearer ";
private static final Logger logger = LoggerFactory.getLogger(JwtAuthFilter.class);
#Autowired
private UserDetailsService jwtUserDetailsService;
#Autowired
private JwtTokenUtil jwtTokenUtil;
#Value ("${jwt.http.request.header}")
private String tokenHeader;
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
final String requestTokenHeader = request.getHeader(this.tokenHeader);
String username = null;
String jwtToken = null;
if((requestTokenHeader != null) && requestTokenHeader.startsWith(BEARER)) {
jwtToken = requestTokenHeader.substring(7);
try {
username = jwtTokenUtil.getUsernameFromToken(jwtToken);
if((username != null) && (SecurityContextHolder.getContext()
.getAuthentication() == null)) {
UserDetails userDetails = this.jwtUserDetailsService.loadUserByUsername(username);
if(jwtTokenUtil.validateToken(jwtToken, userDetails)) {
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =
new UsernamePasswordAuthenticationToken(userDetails, null,
userDetails.getAuthorities());
usernamePasswordAuthenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext()
.setAuthentication(usernamePasswordAuthenticationToken);
}
}
}
catch (IllegalArgumentException e) {
logger.error("Unable to get username from JWT. ", e.getLocalizedMessage());
}
catch (ExpiredJwtException e) {
logger.warn("Expired JWT. ", e.getLocalizedMessage());
}
catch (Exception e) {
logger.error(e.getLocalizedMessage());
}
}
chain.doFilter(request, response);
}
}
#Component
public class JwtUnAuthorizedResponseAuthenticationEntryPoint implements AuthenticationEntryPoint , Serializable {
private static final long serialVersionUID = - 8970718410437077606L;
#Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e)
throws IOException {
if(e instanceof TooManyDevicesException) {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, Constants.TOO_MANY_DEVICES);
}
else if(e instanceof CredentialsChangedException) {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, Constants.CREDENTIALS_CHANGED);
}
else {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, Constants.JWT_EXPIRED);
}
}
}
I want to send an appropriate unauthorized response from my filter, is there a way to do this?
I think once you caught the AuthenticationException in JwtAuthFilter, you should not move forward to the next filter as most probably an AnonymousAuthenticationFilter will sit in the later part of the filter chain and this filter will configure the current request to be an anonymous user if SecurityContextHolder is empty (i.e happen when authentication fail). The InsufficientAuthenticationException is most probably due to Spring considers the current request is an anonymous user who access some protected URL or methods.
Instead , once you catch AuthenticationException in your JwtAuthFilter , you should then call AuthenticationEntryPoint.commence() and end the filter chain . This is also how the BasicAuthenticationFilter is doing now .
So , I suggest revise the JwtAuthFilter to :
#Component
public class JwtAuthFilter extends OncePerRequestFilter {
#Autowired
private AuthenticationEntryPoint authenticationEntryPoint;
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
try{
//validate JWT
} catch (AuthenticationException e) {
logger.error(e.getLocalizedMessage());
SecurityContextHolder.clearContext();
this.authenticationEntryPoint.commence(request, response, e);
return;
}
}
}
As a workaround for now I simply added my custom error from the JwtAuthFilter as a request attribute and retrieved it in the AuthenticationEntryPoint
So I have set up my shiro to have two Realms. A Username and Password Realm, using the standard UsernamePasswordToken. I have also set up a Custom Bearer Authentication Token that works off a token passed in from the user.
If i just use my passwordValidatorRealm it works find, if no user is found throws unknown account, if password doesn’t match throws incorrect credentials, perfect. But as soon as i put in my tokenValidatorRealm it throws a
org.apache.shiro.authc.AuthenticationException: Authentication token of type [class org.apache.shiro.authc.UsernamePasswordToken] could not be authenticated by any configured realms.
In this instance my tokenValidatorRealm returns null as no token was provided, so it moves on to the passwordValidatorRealm and just breaks.
Any ideas why introducing a second Realm will cause my working passwordValidatorRealm to break?
Have tried with different authentication strategies, and no luck there.
Using shiro 1.2.2
EDIT
I have two implementations, one for password and one for token
Password:
public class PasswordAuthorizingRealm extends AuthenticatingRealm {
#Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
if (authenticationToken instanceof UsernamePasswordToken) {
UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) authenticationToken;
String username = usernamePasswordToken.getUsername();
char[] password = usernamePasswordToken.getPassword();
if (username == null) {
throw new AccountException("Null usernames are not allowed by this realm!");
}
//Null password is invalid
if (password == null) {
throw new AccountException("Null passwords are not allowed by this realm!");
}
UserService userService = new UserServiceImpl();
User user = userService.getUserByUsername(username);
if (user == null) {
throw new UnknownAccountException("Could not authenticate with given credentials");
}
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(username, user.getPassword(), "passwordValidatorRealm");
return simpleAuthenticationInfo;
} else {
return null;
}
}
}
and Bearer Token
public class TokenAuthorizingRealm extends AuthorizingRealm {
#Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
if (authenticationToken instanceof BearerAuthenticationToken) {
BearerAuthenticationToken bearerAuthenticationToken = (BearerAuthenticationToken) authenticationToken;
String username = "" + bearerAuthenticationToken.getPrincipal();
User user = userService.getUserByUsername(username);
//User with such username has not found
if (user == null) {
throw new UnknownAccountException("Could not authenticate with given credentials");
}
BearerAuthenticationInfo bearerAuthenticationInfo = new BearerAuthenticationInfo(user);
return bearerAuthenticationInfo;
}
}
Shiro config
[main]
hashService = org.apache.shiro.crypto.hash.DefaultHashService
hashService.hashIterations = 500000
hashService.hashAlgorithmName = SHA-256
hashService.generatePublicSalt = true
hashService.privateSalt = ****
passwordService = org.apache.shiro.authc.credential.DefaultPasswordService
passwordService.hashService = $hashService
passwordMatcher = org.apache.shiro.authc.credential.PasswordMatcher
passwordMatcher.passwordService = $passwordService
authc = my.BearerTokenAuthenticatingFilter
tokenValidatorRealm = my.TokenAuthorizingRealm
passwordValidatorRealm = my.PasswordAuthorizingRealm
passwordValidatorRealm.credentialsMatcher = $passwordMatcher
securityManager.realms = $tokenValidatorRealm,$passwordValidatorRealm
These have been stripped out a bit, removed logging and other unnecessary code
The BearerTokenAuthenticatingFilter, just basically checks if a token has been supplied in the header if has
private void loginUser(ServletRequest request, ServletResponse response) throws Exception {
BearerAuthenticationToken token = (BearerAuthenticationToken) createToken(request, response);
if (token == null) {
String msg = "createToken method implementation returned null. A valid non-null AuthenticationToken "
+ "must be created in order to execute a login attempt.";
throw new IllegalStateException(msg);
}
try {
Subject subject = getSubject(request, response);
subject.login(token);
onLoginSuccess(token, subject, request, response);
} catch (AuthenticationException e) {
HttpServletResponse httpResponse = WebUtils.toHttp(response);
httpResponse.sendRedirect("login");
}
}
BearerAuthenticationInfo class
public class BearerAuthenticationInfo implements AuthenticationInfo {
private final PrincipalCollection principalCollection;
private final User user;
public BearerAuthenticationInfo(User user) {
this.user = user;
this.principalCollection = buildPrincipalCollection(user);
}
public PrincipalCollection getPrincipals() {
return principalCollection;
}
public Object getCredentials() {
return user.getUsername();
}
private PrincipalCollection buildPrincipalCollection(User user) {
Collection<String> principals = new ArrayList<String>();
principals.add(user.getUsername());
return new SimplePrincipalCollection(principals, "tokenValidatorRealm");
}
}
Looks like it is expected behavior.
If you look at the javadoc for ModularRealmAuthenticator:
* #throws AuthenticationException if the user could not be authenticated or the user is denied authentication
* for the given principal and credentials.
*/
protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) throws AuthenticationException {
If you are having problems with the exception, you might need to change the code that calls the authentication to expect this exception.
Left for other searches:
You might have a missing supports method in your TokenAuthorizingRealm class.
Something like
#Override
public boolean supports(AuthenticationToken token) {
return token instanceof BearerAuthenticationToken;
}
should be present.
This discussion help me solve a similar problem. I wanted to authenticate a user by the application itself, not using any Shiro default implementation. To do that we must subclass AuthenticatingRealm, override doGetAuthenticationInfo and declare this realm as the validation one.
public class PasswordAuthorizingRealm extends AuthenticatingRealm {
#Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
In Shiro.ini:
passwordValidatorRealm = my.PasswordAuthorizingRealm