spring authentication for rest api to include hardcoded and lookup - spring

I have a rest API application that authenticates requests by verifying credentials with a database lookup. I need to now add an additional feature that will allow a specific set of credentials to be allowed that is not a record in the database table. Basically I need to hardcode these credentials; but what I found is that the spring authenticationprovider for userDetails does not authenticate this and I am not sure why or how. I added an if statement just before retrieving the dataset result to validate the user credentials but it still does not work.Here is my code:
#SuppressWarnings("deprecation")
#Override
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException
{
Logger logger = LoggerFactory.getLogger("com.reader.AuthService.loadUserByUsername");
logger.info("Request--");
UserDetails userDt = null;
logger.info("USER:" + username);
Connection con = null;
String query = "SELECT * FROM Customers WHERE username= ?";
logger.info("Making SQL request");
try
{
con= dataSource.getConnection();
String password = null;
String authority = null;
int enabled = 0;
try(Connection dbConn = dataSource.getConnection();
PreparedStatement pst = dbConn.prepareStatement(query);)
{
pst.setString(1, username);
ResultSet rs = pst.executeQuery();
if(username.equalsIgnoreCase("sampleUser")){
username = "sampleUser";
password="1234567_sample";
List ls = new ArrayList();
ls.add(new GrantedAuthorityImpl("ROLE_USER"));
userDt = new User(username, password, enabled==1?true:false , true, true, true, ls);
return userDt;
}
if (rs.next())
{
username = rs.getString("username");
password = rs.getString("password");
authority = "ROLE_USER";
enabled = rs.getInt("isactive");
List ls = new ArrayList();
ls.add(new GrantedAuthorityImpl(authority));
userDt = new User(username, password, enabled==1?true:false , true, true, true, ls);
}
else
{
System.out.println("No credentials present in DB for username" + username);
throw new UsernameNotFoundException(
"Bad Credentials", username);
}
}
catch(SQLException e)
{
System.out.println("API: "+e.getMessage());
e.printStackTrace();
}
con.close();
}
catch(UsernameNotFoundException e)
{
throw e;
}
catch(Exception e1)
{
e1.printStackTrace();
}
return userDt;
In the server-context file I declare the user ref service to this class file above:
<beans:bean id="authservice"
class="com.reader.security.AuthService" />
<security:authentication-manager>
<security:authentication-provider
user-service-ref="authservice" />
</security:authentication-manager>
I think the problem starts here in AbstractUserDetailsAuthenticationProvider:
try {
preAuthenticationChecks.check(user);
additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken) authentication);
} catch (AuthenticationException exception) {
if (cacheWasUsed) {
// There was a problem, so try again after checking
// we're using latest data (i.e. not from the cache)
cacheWasUsed = false;
user = retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication);
preAuthenticationChecks.check(user);
additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken) authentication);
} else {
throw exception;
}
}
here is the exception:
org.springframework.security.authentication.DisabledException: User is disabled
could someone please assist or at least explain how this authentication works.
EDIT:
console response:
10:29:04.703 [http-nio-8080-exec-2]
DEBUG o.s.s.w.a.w.BasicAuthenticationFilter - Basic
Authentication Authorization header found for user
'internalUser'
10:29:04.705 [http-nio-8080-exec-2]
DEBUG o.s.s.authentication.ProviderManager - Authentication attempt
using org.springframework.security.authentication.dao.
DaoAuthenticationProvider
10:29:04.707 [http-nio-8080-exec-2] INFO
c.b.f.AuthService.loadUserByUsername - Request--
10:29:04.707 [http-nio-8080-exec-2] INFO
c.b.f.AuthService.loadUserByUsername - USER:internalUser
10:29:04.707 [http-nio-8080-exec-2] INFO
c.b.f.AuthService.loadUserByUsername - Making SQL request
AbandonedObjectPool is
used (org.apache.commons.dbcp.AbandonedObjectPool#3517a554)
LogAbandoned: false
RemoveAbandoned: true
RemoveAbandonedTimeout: 300
10:32:51.434 [http-nio-8080-exec-2]
DEBUG o.s.s.a.d.DaoAuthenticationProvider - User account is disabled
10:37:12.083 [http-nio-8080-exec-2]
DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached
instance of singleton
bean 'userAuthenticationErrorHandler'
10:37:12.083 [http-nio-8080-exec-2]
DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance
of singleton bean 'org.springframework.context.annotation.
internalScheduledAnnotationProcessor'

It's because your code is setting enabled as false as you initialized enabled integer to zero and later below enabled==1?true:false which will always return false. So i think you missed to set enabled = 1 in case of your "sampleUser" inside if statement
int enabled = 0;
if(username.equalsIgnoreCase("sampleUser")){
username = "sampleUser";
password="1234567_sample";
List ls = new ArrayList();
ls.add(new GrantedAuthorityImpl("ROLE_USER"));
userDt = new User(username, password, enabled==1?true:false , true, true, true, ls);
return userDt;
}

loadUserByUsername(String username) gets called when authentication is completed
for authentication purpose you need to override authenticate() and use it as given below
#Component
public class CustomAuthenticationProvider implements AuthenticationProvider{
#Autowired
private CustomUserService userService;
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String username = authentication.getName();
String password = (String) authentication.getCredentials();
CustomUser user = userService.loadUserByUsername(username);
if (user == null || !user.getUsername().equalsIgnoreCase(username)) {
throw new BadCredentialsException("Username not found.");
}
if (!password.equals(user.getPassword())) {
throw new BadCredentialsException("Wrong password.");
}
Collection<? extends GrantedAuthority> authorities = user.getAuthorities();
return new UsernamePasswordAuthenticationToken(user, password, authorities);
}
public boolean supports(Class<?> arg0) {
return true;
}
}
and declare it in security xml
<authentication-manager>
<authentication-provider ref="customAuthenticationProvider"/>
</authentication-manager>

Related

Map LDAP groups to OAuth2 UserDetails in Spring Boot web app

After failed attempts to get CAS Single Sign Out working, we decided to try out OAuth2 using OpenID Connect with our Spring Boot web apps. Now we only need OAuth for SSO and to provide authentication - we're not storing username/password in a db or anything. We are using Google LDAP to grant authorities. I cannot find any solid example of using Google LDAP for authorities with OAuth2. My OAuth2 code is pretty simple so far. Here's my WebSecurityConfig so far:
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests(authorizeRequests -> authorizeRequests
.mvcMatchers("/home").permitAll()
.anyRequest().authenticated())
.oauth2Login(oauthLogin -> oauthLogin.permitAll());
}
and one of my endpoints that works:
#GetMapping("/user/me")
public String userDetails(#AuthenticationPrincipal OAuth2User user) {
Map<String, Object> attributes = user.getAttributes();
String username = (String) attributes.get("email");
return username;
}
Now here is what I used with my CAS and LDAP config. This block of code would pull all LDAP groups and map them to roles using SimpleGrantedAuthority. I've tried a ton of different configs, but nothing has worked.
private LdapContext getLdapReadContext(){
LdapContext ctx = null;
try{
Hashtable<String,String> env = new Hashtable<>();
env.put(Context.INITIAL_CONTEXT_FACTORY,ldapCtxFactory);
env.put(Context.SECURITY_AUTHENTICATION,"Simple");
env.put(Context.SECURITY_PRINCIPAL,ldapUsername);
env.put(Context.SECURITY_CREDENTIALS,ldapPassword);
env.put(Context.PROVIDER_URL,ldapURL);
ctx = new InitialLdapContext(env,null);
}catch(NamingException e){
e.printStackTrace();
}
return ctx;
}
public List<String> Groups(LdapContext ctx, AttributePrincipal principal){
String uid = "uid";
List<String> groups = new LinkedList<>();
NamingEnumeration answer = null;
if(principal!=null){
Map attributes =principal.getAttributes();
if(attributes.get(uid)!=null){
uid=attributes.get(uid).toString();
try{
SearchControls constraints = new SearchControls();
constraints.setSearchScope(SearchControls.SUBTREE_SCOPE);
String searchFilter = "(&(objectClass=groupOfNames)(member=uid="+uid+","+ldapBase+"))";
String[] attrIDs = {"cn"};
constraints.setReturningAttributes(attrIDs);
answer = ctx.search(ldapGroupBase,searchFilter,constraints);
while (answer.hasMore()){
Attributes attrs = ((SearchResult)answer.next()).getAttributes();
groups.add(attrs.get("cn").toString());
}
}catch(Exception ignored){
}finally{
if (answer !=null){
try{
answer.close();
}catch(Exception ignored){
}
}
if (ctx != null){
try{
ctx.close();
}catch(Exception ignored){
}
}
}
}
}
if (groups.isEmpty()){
groups.add("STUDENT");
}
return groups;
}
#Override
public UserDetails loadUserDetails (#Autowired Authentication authentication) throws UsernameNotFoundException {
CasAssertionAuthenticationToken casAssertionAuthenticationToken = (CasAssertionAuthenticationToken) authentication;
AttributePrincipal principal = casAssertionAuthenticationToken.getAssertion().getPrincipal();
Map attributes = principal.getAttributes();
String user = (String)attributes.get("username");
String email = (String)attributes.get("email");
String fname = (String)attributes.get("fname");
String lname = (String)attributes.get("lname");
String VNumber = (String)attributes.get("UDC_IDENTIFIER");
String uid = (String)attributes.get("uid");
String role = "user";
String username = authentication.getName();
List<String> grouplist = null;
grouplist = Groups(getLdapReadContext(), principal);
Collection<SimpleGrantedAuthority> collection = new ArrayList<SimpleGrantedAuthority>();
for (int i =0; i<grouplist.size(); i++) {
collection.add(new SimpleGrantedAuthority((grouplist.get(i)).substring(4)));
}
return new User(username, "",collection);
}

The spring boot login process has this error: org.springframework.security.authentication.BadCredentialsException: Bad credentials",

I am trying to implement a login functionality, from the stored user credentials.
but it gives error as :
org.springframework.security.authentication.BadCredentialsException: Bad credentials",
note:
Password is stored encrypted in database.
Controller code:
#PostMapping("/login")
public ResponseEntity<Map<String, Object>> login(#RequestBody JwtRequest authenticationRequest) throws Exception {
String encodedPassword = this.passwordEncoder.encode( authenticationRequest.getPassword());
String userName = authenticationRequest.getUsername();
try {
authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
userName,
encodedPassword
)
);
}catch(Exception e){
throw new CustomErrorException(
HttpStatus.INTERNAL_SERVER_ERROR,
"the authentication process has errors : "+e.toString()
);
}
final Employee emp = employeeService.findOne(authenticationRequest.getUsername());
if(emp != null){
System.out.println(" *** the employee ******** "+emp.toString());
}else{
System.out.println(" *** the employee not found ******** ");
}
final CustomeEmployee cEmp = customeEmployeeService.loadUserByUsername(authenticationRequest.getUsername());
final String token = jwtTokenUtil.generateToken(cEmp);
System.out.println("the request cam e along wy step 2");
/** -------------------------- */
ArrayNode authorities = mapper.createArrayNode();
for(GrantedAuthority authority : cEmp.getAuthorities()) {
authorities.add(authority.getAuthority());
}
Map<String, Object> result = getConfiguration(emp, cEmp);
result.put("token", token);
return ResponseEntity.ok(result);
}
Appearantly the process is not passing throug the
authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
userName,
encodedPassword
)
);
It says the credential errors.
Note: Password and username is 100% correct.

Keycloak.json configuration file import

I have configured my goclient with keycloak.I am reading the keycloak server details from keycloak.json file which is present inside my Go client.
My whole setup is in Kubernetes cluster.Everytime when new keycloak server comes up how should I pass keycloak.json file to the Go client so that it can read the details from json file?
Is there a way I can get the keycloak.json file with all the client configuration details without having to login to keycloak and download it ?
"realm": "SolutionAdmin",
"auth-server-url": "localhost:8081/auth",
"ssl-required": "none",
"resource": "admin",
"credentials": {
"secret": "6ee0f523-c392-4406-bb14-ba315125c6ea"
},
"confidential-port": 0
}
if you want to have a pre-configured embedded keycloak server, you'll need to extend KeycloakApplication and have it configured to trigger in lieu of the base class. in this example, KeycloakProperties is just a representation of all the keycloak properties we used in application.properties. but you can get the gist of it. fair warning: i did not write this, but am figuring out how a coworker did for another project.
public class EmbeddedKeycloakApplication extends KeycloakApplication {
public final KeycloakProperties keycloakProperties;
public EmbeddedKeycloakApplication() {
super();
keycloakProperties = SpringContextAdapter.getBean(KeycloakProperties.class);
System.getProperties().putAll(keycloakProperties.getRealmProperties());
createMasterRealmAdminUser();
createRealm();
}
private void createMasterRealmAdminUser() {
log.debug("Creating administrative user.");
String username = keycloakProperties.getServer(KeycloakProperties.ADMIN_USER_USERNAME);
String password = keycloakProperties.getServer(KeycloakProperties.ADMIN_USER_PASSWORD);
String email = keycloakProperties.getServer(KeycloakProperties.ADMIN_USER_EMAIL);
KeycloakSession session = getSessionFactory().create();
ApplianceBootstrap applianceBootstrap = new ApplianceBootstrap(session);
try {
session.getTransactionManager().begin();
if (!applianceBootstrap.isNoMasterUser()) {
log.debug("Administrative user already exists. No work to do.");
return;
}
applianceBootstrap.createMasterRealmUser(username, password);
RealmModel adminRealm = session.realms().getRealm(Config.getAdminRealm());
UserModel adminUser = session.users().getUserByUsername(username, adminRealm);
adminUser.setEmail(email);
adminUser.setEmailVerified(true);
session.getTransactionManager().commit();
log.info("Created administrative user {}", username);
} catch (Exception ex) {
log.error("Couldn't create keycloak master admin user: {}", ex.getMessage());
session.getTransactionManager().rollback();
}
session.close();
}
private void createRealm() {
String realmImportFilename = keycloakProperties.getRealmImportFile();
KeycloakSession session = getSessionFactory().create();
String realmId = keycloakProperties.getRealm(KeycloakProperties.REALM_ID);
try {
session.getTransactionManager().begin();
RealmManager manager = new RealmManager(session);
if (manager.getRealm(realmId) != null) {
log.debug("{} realm already exists. No work to do.", realmId);
return;
}
Resource realmImportFile = new ClassPathResource(realmImportFilename);
RealmRepresentation rep =
JsonSerialization.readValue(
realmImportFile.getInputStream(), RealmRepresentation.class, true);
manager.importRealm(rep);
log.info("Imported Realm json file {}", realmImportFilename);
session.getTransactionManager().commit();
} catch (Exception e) {
log.error("Failed to import Realm json file {}: {}", realmImportFilename, e.getMessage(), e);
session.getTransactionManager().rollback();
}
session.close();
}
}
#Configuration
public class EmbeddedKeycloakConfig {
#Bean
ServletRegistrationBean<HttpServlet30Dispatcher> keycloakJaxRsApplication(
KeycloakProperties keycloakProperties, DataSource dataSource) throws NamingException {
mockJndiEnvironment(dataSource);
var contextPath = keycloakProperties.getServer(KeycloakProperties.SERVER_CONTEXT_PATH);
ServletRegistrationBean<HttpServlet30Dispatcher> servlet =
new ServletRegistrationBean<>(new HttpServlet30Dispatcher());
servlet.addInitParameter(
"javax.ws.rs.Application", EmbeddedKeycloakApplication.class.getName());
servlet.addInitParameter(
ResteasyContextParameters.RESTEASY_SERVLET_MAPPING_PREFIX, contextPath);
servlet.addInitParameter(ResteasyContextParameters.RESTEASY_USE_CONTAINER_FORM_PARAMS, "true");
servlet.addInitParameter(ResteasyContextParameters.RESTEASY_DISABLE_HTML_SANITIZER, "true");
servlet.addUrlMappings(contextPath + "/*");
servlet.setLoadOnStartup(1);
servlet.setAsyncSupported(true);
return servlet;
}
#Bean
FilterRegistrationBean<EmbeddedKeycloakRequestFilter> keycloakSessionManagement(
KeycloakProperties keycloakProperties) {
FilterRegistrationBean<EmbeddedKeycloakRequestFilter> filter = new FilterRegistrationBean<>();
filter.setName("Keycloak Session Management");
filter.setFilter(new EmbeddedKeycloakRequestFilter());
filter.addUrlPatterns(
keycloakProperties.getServer(KeycloakProperties.SERVER_CONTEXT_PATH) + "/*");
return filter;
}
private void mockJndiEnvironment(DataSource dataSource) throws NamingException {
NamingManager.setInitialContextFactoryBuilder(
env ->
environment ->
new InitialContext() {
#Override
public Object lookup(Name name) {
return lookup(name.toString());
}
#Override
public Object lookup(String name) {
if ("spring/datasource".equals(name)) {
return dataSource;
}
return null;
}
#Override
public NameParser getNameParser(String name) {
return CompositeName::new;
}
#Override
public void close() {
// NOOP
}
});
}
}

How to prioritize interceptors in Spring web mvc

I have three interceptors in my application and i just want to prioritize them, actually i want to auto login my application from another application via query params.
This interceptor is validating the user session if user doesn't have valid session then it will redirect user to login page and it is working fine.
public class ValidateSessionInterceptor extends HandlerInterceptorAdapter {
private Logger log = Logger.getLogger(getClass());
#Value("${http.port}")
private int httpPort;
#Value("${https.port}")
private int httpsPort;
#Value("${use.ssl}")
private boolean useSsl;
//before the actual handler will be executed
public boolean preHandle(HttpServletRequest request,HttpServletResponse response, Object handler) throws Exception {
HttpSession session = request.getSession();
if(session.getAttribute("user")==null){
String forwardTo = (String) request.getAttribute("org.springframework.web.servlet.HandlerMapping.pathWithinHandlerMapping");
String params = "";
if(request.getQueryString()!=null){
params = "?" + request.getQueryString();
}
String url = getApplicationUrl(request,useSsl)+forwardTo+params;
log.info("redirect url: " + request.getContextPath()+"/login/index.mars?forwardTo="+URLEncoder.encode(url, "UTF-8"));
response.sendRedirect(request.getContextPath()+"/login/index.mars?forwardTo="+URLEncoder.encode(url, "UTF-8"));
return false;
}else{
Map<String,String> owners = new LinkedHashMap<String,String>();
owners.put("NA", "NA");
owners.put("AK", "AK");
request.setAttribute("ownerList", owners);
}
return true;
}
private String getApplicationUrl(HttpServletRequest request,boolean useSsl){
if(useSsl){
return "https://"+request.getServerName()+":"+httpsPort+request.getContextPath();
}else{
return "http://"+request.getServerName()+":"+httpPort+request.getContextPath();
}
}
}
This is being called by another application and passing autoUsr and autoPwd parameters to auto logged in application.
public class AutoLoginInterceptor extends HandlerInterceptorAdapter{
private final Logger log = Logger.getLogger(getClass());
#Autowired
public UserService userService;
#Autowired
public WebService webService;
public boolean preHandle(HttpServletRequest request,HttpServletResponse response, Object handler) throws IOException, UserException {
HttpSession session = request.getSession();
if(session.getAttribute("user")==null){
String forwardTo = request.getParameter("forwardTo");
if(forwardTo!=null && !forwardTo.equals("")){
User user = checkLoginCrendential(forwardTo);
log.info("user-> " + user);
this.webService.buildWebService(request);
if(userService.login(request, user)){
session.setAttribute("user", user);
return true;
}
}
}
return true;
}
public User checkLoginCrendential(String url){
String decURL;
User user = new User();
try
{
decURL = URLDecoder.decode(url,"utf-8");
String params[] = (decURL.split("\\?")[1]).split("&");
String loginParams[] = {"autoUsr","autoPwd"};
for(String lgnParam : loginParams){
for(int i = 0 ; i < params.length ; i++){
String param[] = params[i].split("=");
if(lgnParam.equals(param[0])){
if(param.length > 1){
if(lgnParam.equals("autoUsr")){
user.setUsername(param[1]);
}else if(lgnParam.equals("autoPwd")){
user.setPassword(param[1]);
}
}else{
if(lgnParam.equals("autoUsr")){
user.setUsername("");
}else if(lgnParam.equals("autoPwd")){
user.setPassword("");
}
}
}
}
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return user;
}
}
You can use tag to order interceptors in XXX-servlet.xml. For example :
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**" />
<bean class="ValidateSessionInterceptor" />
</mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/**" />
<bean class="AutoLoginInterceptor" />
</mvc:interceptor>
</mvc:interceptors>
interceptors will be called by order

session.getAttribute returns null

I'am using jsp and servlet to realize the authentication before any access to the application
The is the code of my doPost method:
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String username = request.getParameter("username");
String password = request.getParameter("password");
Account account;
try {
//Checking if the user already exists
account = accountBesinessLocal.findByLogin(String.valueOf(username));
if (account != null) {
logger.log(Level.WARNING, "[User exists:{0}]", username);
if (accountBesinessLocal.authentificateUser(String.valueOf(username), String.valueOf(password))) {
HttpSession session = request.getSession(true);
System.out.println(session);
session.setAttribute("username", account.getLogin());
session.setAttribute("password", account.getPassword());
System.out.println("this "+session.getAttribute(username)+" is connected");
response.sendRedirect(home.xhtml");
} else {
request.setAttribute("erreur", "Incorrect Authentication");
getServletContext().getRequestDispatcher("/loginForm.jsp").forward(request, response);
}
} else {
request.setAttribute("erreur", "Incorrect Authentication");
logger.log(Level.WARNING, "[User does not exist:{0}]", username);
getServletContext().getRequestDispatcher("/loginForm.jsp").forward(request, response);
}
} finally {
}
}
When i try to get the login of the user conneced with session.getAttribute(username);
it returns null.
How can i solve this?
You must use
session.getAttribute("username")
and not
session.getAttribute(username)
the value of username is whatever the user has entered in the login input field. It's not "userame".
Side note: your code doesn't compile, s you might be running code that isn't the one ou posted.

Resources