Spring Boot Dynamically load authorization URL's with antMatchers - java

I have been trying to develop an application which shall load the authorization URL's from database. This application shall not have any user interface.
When I try to send request's through postman it gives me an error of "403". If I add
.antMatchers("/login").permitAll();
in the top then the application permits all request which is authenticated successfully whether the user have that authorization or not. Below my codes are given
WebSecurityConfig.cass
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter{
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/login").permitAll();
//Module List are holding modules which shall provide the URL's and authority names
List<Module> moduleList = _moduleDAO.getModule();
for (Module module : moduleList) {
if(module.getUrl() != null & module.getModuleName() != null) {
String url = module.getUrl();
String authority = module.getModuleName();
String requestType = module.getRequestType();
if(requestType != null) {
if(requestType == "GET") {
http.authorizeRequests().antMatchers(HttpMethod.GET,url).hasAuthority(authority);
}else if(requestType == "POST") {
http.authorizeRequests().antMatchers(HttpMethod.POST, url).hasAuthority(authority);
}else if(requestType == "DELETE") {
http.authorizeRequests().antMatchers(HttpMethod.DELETE,url).hasAuthority(authority);
}else if(requestType == "PUT") {
http.authorizeRequests().antMatchers(HttpMethod.PUT, url).hasAuthority(authority);
}
}
logger.info("-- Adding URL : " +url +" Request Type : "+requestType+" With Authority : " + authority);
}
}
http
.authorizeRequests()
.anyRequest().denyAll().and()
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout")).logoutSuccessUrl("/login");
}
UserPrinciple.class
public class UserPrinciple implements UserDetails{
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
//Get Roles
if(this.roleList != null) {
this.roleList.stream().distinct().forEach(role -> {
if(role != null && role.getRole() != null) {
String RoleName = "ROLE_"+role.getRole();
logger.info("#Adding "+RoleName + " to the authority");
GrantedAuthority authority = new SimpleGrantedAuthority(RoleName);
authorities.add(authority);
}
});
//Get Modules
if(this.moduleList != null) {
this.moduleList.forEach(module ->{
String moduleName = module.getModuleName();
logger.info("#Adding "+moduleName + " to the authority");
GrantedAuthority authority = new SimpleGrantedAuthority(moduleName);
authorities.add(authority);
});
}
}
return authorities;
}
In database I have stored these modules as described in the below link:
https://pasteboard.co/JdHgh5W.png

I don't know why but it appears that the only fault I had done is in the if condition. While debugging I have found that my statements under these if condition was not being executed. So I have changed my code statement approach.
I have just changed this
if(requestType == "GET") {
into
if(requestType.equals("GET")) {
It turns out that there was some problem in database too. The request types were not matching in "if condition" maybe in database the string was stored in different format. I have copied the request type string (i.e. GET, POST) from the log console of the IDE and pasted into the database columns.
And Everything started to work perfectly now. :V

Related

getting authenticated user inside the configuration class in Spring

I am using Spring Security to authenticate users. I need to resolve which user has authenticated in my ApplicationConfiguration to provide the correct data, but for some reason, the following code returns null:
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
EDIT:
I basically want to inject a service into a controller. (PackagesBaseService)
This service is abstract, so in my bean definition (In configuration class) I need to resolve if PackagesBaseService is an instance of TablePackagesService or DeskPackagesService. This is based on which user is authenticated (this requirement cannot be changed).
I understand I could just test the Authenticated user even in my controller and instantiate my service there, but I would like to avoid that.
I am able though to retrieve the Auth user using this same from anywhere else.
Why I can't use this from a configuration file? Does it have something with the order that the beans are loaded?
How can I solve this?
Implementation:
Configuration:
#Bean
public PackagesBaseService packagesBaseService()
{
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication != null && !(authentication instanceof AnonymousAuthenticationToken)) {
if (authentication.getName() == env.getProperty("tableFactory.username")) {
return new TablePackagesService();
}
if (authentication.getName() == env.getProperty("deskFactory.username")) {
return new DeskPackagesService();
}
// thrown Exception
}
// thrown Exception
}
Controller:
#Autowired
PackagesBaseService packagesService;
public MultiPackagesResponse data(#RequestParam("fromId") int fromId)
{
MultiPackagesResponse response = packagesService.getPackages(fromId);
return response;
}
You need to go with a factory to be able to use the user context. This COULD look like this:
Define a Factory bean:
#Service
public class PackageBaseServiceFactory {
public final HashMap < String,
PackageBaseService > packageBaseServiceCache = new HashMap();
public PackageBaseService getPackageService() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication != null && !(authentication instanceof AnonymousAuthenticationToken)) {
PackageBaseService packageBaseService = packageBaseServiceCache.get(authentication.getName());
if (packageBaseService == null) {
packageBaseService = initPackagesBaseService(authentication.getName());
}
return packageBaseService;
}
}
private PackageBaseService initPackagesBaseService(String authenticationName) {
PackageBaseService packageBaseService;
if (authenticationName == env.getProperty("tableFactory.username")) {
packageBaseService = new TablePackagesService();
} else if (authenticationName == env.getProperty("deskFactory.username")) {
packageBaseService = new DeskPackagesService();
} else {
throw new IllegalStateException(); //or whatever you do
}
return packageBaseServiceCache.put(authenticationName, packageBaseService);
}
}
and used it this way
#Autowired
PackageBaseServiceFactory packageBaseServiceFactory;
public MultiPackagesResponse data( # RequestParam("fromId")int fromId) {
MultiPackagesResponse response = packageBaseServiceFactory.getPackageService().getPackages(fromId);
return response;
}

cannot make authentication work

I am struggling for a few hours to create a small authentication system using Spring Security but I have not had luck yet.
What I want is a login form which goes through AngularJS to a database and searches with the provided data. For the purpose of making it work, I tried to use an in memory database.
Here is the code I used:
#RestController
#RequestMapping("/authentication")
public class AuthenticationController {
#RequestMapping("/user")
public Principal user(Principal user) {
return user;
}
#Configuration
#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
protected static class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/index.html", "/home.html", "/login.html", "/lib/**", "/app/**", "/css/**", "/js/**").permitAll().anyRequest()
.authenticated();
http.formLogin().loginPage("/login.html").defaultSuccessUrl("/index.html")
.failureUrl("/login?error").permitAll();
// Logout
http.logout().logoutUrl("/logout").logoutSuccessUrl("/login?logout")
.permitAll();
}
#Override
protected void configure(AuthenticationManagerBuilder auth)
throws Exception {
// Authorization
auth.inMemoryAuthentication().withUser("user").password("p1")
.roles("USER");
auth.inMemoryAuthentication().withUser("root").password("p2")
.roles("USER", "ADMIN");
}
private CsrfTokenRepository csrfTokenRepository() {
HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
repository.setHeaderName("X-XSRF-TOKEN");
return repository;
}
}
}
This is the authenticate function from Angular:
function($rootScope, $scope, $http, $location) {
var authenticate = function(credentials, callback) {
var headers = credentials ? {
authorization : "Basic "
+ btoa(credentials.username + ":"
+ credentials.password)
} : {};
$http.get('authentication/user', {
headers : headers
}).success(function(data) {
console.log(data.name);
if (data.name) {
$rootScope.authenticated = true;
} else {
$rootScope.authenticated = false;
}
callback && callback();
}).error(function() {
$rootScope.authenticated = false;
callback && callback();
});
}
authenticate();
$scope.credentials = {};
$scope.login = function() {
authenticate($scope.credentials, function() {
if ($rootScope.authenticated) {
$location.path("/index.html");
$scope.error = false;
} else {
//$location.path("/login");
$scope.error = true;
}
});
};
});
Do you have any idea why this isn't working? The provided code I took from a few tutorials which are very similar. I tried a slightly different way previous to this which used httpBasic but still am not getting the desired result.
Any advice? Thank you!
It is quite late in my timezone and cannot test & execute your code :), but from my point of view you do not have a chance to run your code properly.
You are inappropriately mixing basic authentication with form login authentication. You declare form login:
http.formLogin().loginPage("/login.html").defaultSuccessUrl("/index.html")
.failureUrl("/login?error").permitAll();
which briefly means that you require form login with POST to /login.html page.
Form login means that you should post a form using angular with username & password fields included (as if you posted a normal form using POST method). Therefore you should have something like this in authenticate function:
var authenticate = function(credentials, callback) {
$http.post('login.html', {username: username, password: password} ).success(function(data) {
// do something
}).error(function() {
// do something
});
}
Just please be forgiving if something does not compile above because I simply cannot test it/verify it.

How to show a different page for a type of user in spring security

I'm working in a spring mvc project using spring security, i am new to spring security and i wanted to know how to have two types of users in my application, a normal-user and an admin-user and show a different index page to the admin-user and another index page with less functions to the normal user, so far i have this:
My configSecurity class WebSecurityConfigurerAdapter
public class ConfigSecurity extends WebSecurityConfigurerAdapter {
private AutenticarProvider authen;
#Override
protected void configure( HttpSecurity http ) throws Exception
{
http
.authenticationProvider(authen)
.authorizeRequests()
.antMatchers("/resources/**").permitAll()
.antMatchers("/css/**").permitAll()
.antMatchers("/js/**").permitAll()
.antMatchers("/img/**").permitAll()
.antMatchers("/sound/**").permitAll()
.antMatchers("/fonts/**").permitAll()
.antMatchers("/ajax/**").permitAll()
.antMatchers("/php/**").permitAll()
.antMatchers("/xml/**").permitAll()
.antMatchers("/admin/**").access("hasRole('ROLE_ADMIN')") <-- i am not sure about this just guessing
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/loginPage")
.permitAll()
.and()
.logout()
.permitAll();
}
}
And my class that implements AuthenticationProvider:
#Component
public class AutenthenProvider implements AuthenticationProvider
{
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
User user = null;
Authentication auth = null;
String name = null;
String password = null;
try
{
name = authentication.getName();
password = authentication.getCredentials().toString();
if(name != null && !name.trim().equals("") && password != null && !password.trim().equals(""))
{
user = this.obtainUserFromDataBase(name);
if(user != null)
{
List<GrantedAuthority> grantedAuths = new ArrayList<GrantedAuthority>();
auth = new UsernamePasswordAuthenticationToken(name, password);
}
else
{
throw new UsernameNotFoundException("the user dont exist");
}
}
else
{
throw new BadCredentialsException("invalid credentials");
}
}
catch (AuthenticationException e) {
throw e;
}
catch (Exception ex) {
throw new AuthenticationServiceException("", ex.getCause());
}
return auth;
}
and my controller method from my controller class
#RequestMapping(value = "/loginPage", method = RequestMethod.GET)
public String loginPage(Model model) {
logger.info("**Login PAGE!!**");
return "loginPage";
}
I am thinking of putting this line .antMatchers("/admin/**").access("hasRole('ROLE_ADMIN')") in my configure method but i am not sure how this works, if i put that does that mean that i am going to have duplicate pages because there are pages in my application that can be view by both users does this mean that i am going to have those two pages duplicated but in different folders?
You can use tags that Spring has builtin for the purpose based on roles e.g. admin or user. You can also define custom roles.
<sec:authorize access="hasRole('supervisor')">
This content will only be visible to users who have
the "supervisor" authority in their list of <tt>GrantedAuthority</tt>s.
</sec:authorize>
You might also have luck with the hasRole([role]) method for a code-based solution or have a look at this answer How to check "hasRole" in Java Code with Spring Security?
First, the correct way is to use roles. In you AuthenticationProvider, you give a GrantedAuthority ROLE_ADMIN to admin users, by slightly modifying it :
List<GrantedAuthority> grantedAuths = new ArrayList<GrantedAuthority>();
// only if user is recognized as admin
grantedAuths.add(new SimpleGrantedAuthority("ROLE_ADMIN")
auth = new UsernamePasswordAuthenticationToken(name, password);
Then you will be able to use .antMatchers("/admin/**").access("hasRole('ROLE_ADMIN')") to restrict access to /admin/** to admin users, and you can put in a JSP page
<sec:authorize access="hasRole('ROLE_ADMIN')">
This content will only be visible to users who have
the "ROLE_ADMIN" authority in their list of <tt>GrantedAuthority</tt>s.
</sec:authorize>
as proposed by 909 Niklas
Anyway, I almost never had to implement an AuthenticationProvider. I generally use a DaoAuthenticationProvider and a relevant UserDetailsService (InMemoryUserDetailsManager for tests and JdbcUserDetailsManager when the users are stored in a true database).

Spring 3.5. When user login then logout and try to login once again he get an error about user\pass

I have spring(4.0.6) web application. I've noticed that when user logon then logout and try to logon once again gets an error about user\pass.
I don't have web.xml file because I am using SpringBootServletInitializer class to config my application.
I've add to my configuration such bean
#Bean
public ServletListenerRegistrationBean<HttpSessionEventPublisher> httpSessionEventPublisher()
{
return new ServletListenerRegistrationBean<HttpSessionEventPublisher>(new HttpSessionEventPublisher());
}
and in security config I have this:
http.sessionManagement()
.maximumSessions(1).expiredUrl("/login?expired")
.maxSessionsPreventsLogin(true)
.and()
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
.invalidSessionUrl("/");
As I understand previous session is alive and because of Maximum session = 1 user can't login once again. What should I do to avoid it?
You should invalidate the user session when he logout. Below is the piece of code. Need to pass the userid.
UserInfo is the POJO
public boolean invalidateUserSession(Long userId) {
List<Object> principalsList = sessionRegistry.getAllPrincipals();
Object targetPrincipal = null;
for (Object principal : principalsList) {
if (principal instanceof UserInfo) {
if (((UserInfo) principal).getId().equals(userId)) {
targetPrincipal = principal;
break;
}
}
}
if (targetPrincipal == null) {
return false;
}
List<SessionInformation> userSessionsList = sessionRegistry.getAllSessions(targetPrincipal, false);
for (SessionInformation x : userSessionsList) {
x.expireNow();
}
return true;
}

Please ensure that at least one realm can authenticate these tokens

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

Categories