Spring Security 4 xml configuration UserDetailsService authentication not working - spring-mvc

I know that this question was asked many times but any answer solved my problem.
I am trying to implement a custom login form using Spring Security 4 + Hibernate + Spring Data Jpa, but things don't work as I expect.
When I use in-memory credentials all work fine but I want to use my database instead.
Below the main code :
Xml security config.
<beans:bean id="encodeurMotDePasse" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder">
<beans:constructor-arg name="strength" value="12" />
</beans:bean>
<security:http auto-config="true" create-session="never">
<security:intercept-url pattern="/" access="permitAll" />
<security:intercept-url pattern="/inscription**" access="hasRole('ADMIN') or hasRole('USER')" />
<security:intercept-url pattern="/connexion**" access="hasRole('USER') or hasRole('USER')" />
<security:intercept-url pattern="/test**" access="hasRole('ADMIN')" />
<security:intercept-url pattern="/dba**" access="hasRole('ADMIN')" />
<security:form-login login-page="/login.html"
username-parameter="identifiant"
password-parameter="motDePasse"
authentication-failure-url="/login.html?error=t"/>
</security:http>
<beans:bean id="customUserDetailsService" class="com.app.security.CustomUserDetailsService"/>
<security:authentication-manager >
<security:authentication-provider user-service-ref ="customUserDetailsService">
<security:password-encoder ref="encodeurMotDePasse" />
</security:authentication-provider>
</security:authentication-manager>
The UserDetailsService implementation :
#Service
public class CustomUserDetailsService implements UserDetailsService {
#Autowired
private ServicesDAO service;
#Override
public UserDetails loadUserByUsername( String username ) throws UsernameNotFoundException {
T_authentification userPrincipals = service.getAuthenticatePrincipal( username );
if ( userPrincipals == null ) {
System.out.println( "user inexistant" );
throw new UsernameNotFoundException( "L'utilisateur n'a pas été trouvé" );
} else {
System.out.println( "user trouvé" );
}
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
for ( T_roles role : userPrincipals.getRoles() ) {
System.out.println( " role dans userdetails service est :" + role.getRoleName() );
authorities.add( new SimpleGrantedAuthority( role.getRoleName() ) );
}
// return new CustomUserDetails( userPrincipals );
return new org.springframework.security.core.userdetails.User( userPrincipals.getUsername(), userPrincipals.getMotDePasse(), authorities );
}
}
When I test the code in a controller method all credentials are well loaded from the database and I can print them on the console.
The other concern is when the login fails, Spring Security doesn't send any debug message in the console to tell the cause of this failure.
EDIT
Here my log4j.xml, I follow the configuration but any message appears in the console and the file are also empty.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration PUBLIC "-//APACHE//DTD LOG4J 1.2//EN" "http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/xml/doc-files/log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/" debug="true">
<appender name="Appender1" class="org.apache.log4j.ConsoleAppender">
<param name="Threshold" value="debug" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%-7p %d [%t] %c %x - %m%n"/>
</layout>
</appender>
<appender name="SpringAppender" class="org.apache.log4j.FileAppender">
<param name="file" value="C:/Log4j/Spring-details.log" />
<param name="Threshold" value="debug" />
<param name="append" value="true" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern"
value="%d{MM/dd/yyyy HH:mm:ss} [%t]:%c{5}.%M()%L %m%n" />
</layout>
</appender>
<appender name="Appender2" class="org.apache.log4j.FileAppender">
<param name="File" value="C:/Log4j/app.log" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%-7p %d [%t] %c %x - %m%n"/>
</layout>
</appender>
<category name="org.springframework">
<priority value="ALL" />
</category>
<category name="org.springframework">
<priority value="debug" />
</category>
<category name="org.springframework.beans">
<priority value="debug" />
</category>
<category name="org.springframework.security">
<priority value="debug" />
</category>
<category
name="org.springframework.beans.CachedIntrospectionResults">
<priority value="debug" />
</category>
<category name="org.springframework.jdbc.core">
<priority value="debug" />
</category>
<category name="org.springframework.transaction.support.TransactionSynchronizationManager">
<priority value="debug" />
</category>
<logger name="org.springframework" additivity="false">
<level value="DEBUG"/>
<appender-ref ref="SpringAppender"/>
</logger>
<root>
<!-- <priority value="INFO"/> -->
<level value="DEBUG"/>
<appender-ref ref="Appender1" />
<appender-ref ref="SpringAppender" />
<appender-ref ref="Appender2" />
</root>
</log4j:configuration>
EDIT2
I got this exception when I try to #Autowire this bean <beans:bean id="customUserDetailsService" class="com.app.security.CustomUserDetailsService"/> in a java class.
Why this error occured?
ERROR javax.enterprise.web.core - ContainerBase.addChild: start:
org.apache.catalina.LifecycleException: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'inscriptionController': Unsatisfied dependency expressed through field 'customUserDetailsService'; nested exception is org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'customUserDetailsService' is expected to be of type 'com.app.security.CustomUserDetailsService' but was actually of type 'com.sun.proxy.$Proxy323'
at org.apache.catalina.core.StandardContext.start(StandardContext.java:5985)
Many thanks for your clarifications and sorry for my bad English.

I found the reason of this strange behavior.
I suppose that the spring security listener org.springframework.security.web.session.HttpSessionEventPublisherdoesn't share the same context as the servlet dispatcher. So, all beans defined in the mvc context are inaccessible for the security part.
To solve this, I had to add the <context:component-scan base-package=.... in the root context as all beans defined here are accessible everywhere.
I lost 2 weeks to find the reason of my problem :( !

Related

How to Make log4Net\log4Net.Async work after IIS service Recycle?

I use log4net and log4net.Async in my application to logs.It works good on my local environment(VS2017).When i release it and publish it to the IIS server,it works well at the starte time.But after i recycle the service,it can't write logs anymore,the log file is still there but has no log message inside.
I set the log folder whith permission(READ\WIRTE) for EVERYONE already.
there is my log config:
<appender name="InfoRollingFileAppender" type="log4net.Appender.RollingFileAppender">
<lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
<param name="Encoding" value="utf-8" />
<param name="File" value="D:\Log\Info\" />
<param name="AppendToFile" value="true" />
<param name="rollingStyle" value="Date" />
<param name="datePattern" value="yyyyMMdd\\yyyy-MM-dd-HH.'Info.log'" />
<param name="staticLogFileName" value="false" />
<layout type="AsPay.GateWay.Log.AsRefundAccLayout">
<conversionPattern value="--logtime:%date --:%-5level--msg:%Logtxt --:%CallType%newline" />
</layout>
<filter type="log4net.Filter.LevelRangeFilter">
<param name="LevelMin" value="INFO" />
<param name="LevelMax" value="INFO" />
</filter>
</appender>
<appender name="asyncForwarder" type="Log4Net.Async.AsyncForwardingAppender,Log4Net.Async">
<appender-ref ref="InfoRollingFileAppender" />
</appender>
<root>
<level value="DEBUG" />
<appender-ref ref="asyncForwarder" />
</root>
code in Global.asax:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
System.IO.FileInfo fileinfo = new System.IO.FileInfo(Server.MapPath("~/log4net.Config"));
log4net.Config.XmlConfigurator.Configure(fileinfo);
}
The strange thing is after recycle the IIS,the log file(eg:2019-04-10-00.Info.log) is continued to be created,but there is no log content in the logfile.After recycle the IIS, There are no problems with the functionality of the service,except the log.

Log4net is not logging anything

I cannot get log4net working on my ASP.NET project. My web.config looks like this:
<configSections>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/>
</configSections>
<log4net debug="true">
<appender name="console" type="log4net.Appender.ConsoleAppender">
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date %level %logger - %message%newline" />
</layout>
</appender>
<appender name="file" type="log4net.Appender.RollingFileAppender">
<file value="CardGame.log" />
<appendToFile value="true" />
<rollingStyle value="Size" />
<maxSizeRollBackups value="5" />
<maximumFileSize value="10MB" />
<staticLogFileName value="true" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %level %logger - %message%newline" />
</layout>
</appender>
<root>
<level value="ALL"/>
<appender-ref ref="console" />
<appender-ref ref="file" />
</root>
</log4net>
And my LogHelper class looks this:
public class LogHelper
{
private static LogHelper _instance = new LogHelper();
private static volatile object _lock = new object();
public ILog Logger;
public static LogHelper Instance
{
get
{
lock (_lock)
{
if (_instance == null)
{
_instance = new LogHelper();
}
}
return _instance;
}
}
private LogHelper()
{
Logger = LogManager.GetLogger(typeof(LogHelper));
Logger.Debug("Debug message");
Logger.Warn("Warn message");
Logger.Error("Error message");
Logger.Fatal("Fatal message");
}
}
I have also added the line
log4net.Config.XmlConfigurator.Configure();
to Application_Start in global.asax.cs
However none of the loggings in the constructor works and I am running out of ideas on why it does not work.
Make sure that the user running the app has write permissions on the target folder.
I always test that case by adding everyone and restarting the pool. If it works you know it's a permissions issue, otherwise check the code/config.
Which version are you using ? this is setting from my web.config
<log4net>
<appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
<param name="File" value="Log.txt" />
<param name="AppendToFile" value="true" />
<rollingStyle value="Size" />
<maxSizeRollBackups value="30" />
<maximumFileSize value="15MB" />
<staticLogFileName value="true" />
<layout type="log4net.Layout.PatternLayout">
<!--<conversionPattern value="%-5p %d %5rms [%thread] %-22.22c{1} %-18.18M - %m%n" />-->
<conversionPattern value="%date [%thread] %-5level %logger - %message%newline" />
</layout>
</appender>
<root>
<!-- Options are "ALL", "DEBUG", "INFO", "WARN", "ERROR", "FATAL" and "OFF". -->
<level value="ALL" />
<appender-ref ref="RollingFileAppender" />
</root>
</log4net>

Log4Net Configuration - No system Log file created

I'm having trouble getting log4net to create the log file when errors are recorded.
I'm using ninject to inject dependancies for my objects related to log4net, as ive created a wrapper:-
public interface ILogManager
{
ILog GetLog(Type typeAssociatedWithRequestLog);
}
public class LogManagerAdapter : ILogManager
{
public ILog GetLog(Type typeAssociatedWithRequestLog)
{
var log = LogManager.GetLogger(typeAssociatedWithRequestLog);
return log;
}
}
In NinjectConfigurator.cs :-
private void ConfigureLog4net(IKernel container)
{
XmlConfigurator.Configure();
var logManager = new LogManagerAdapter();
container.Bind<ILogManager>().ToConstant(logManager);
}
private void AddBindings(IKernel container)
{
ConfigureLog4net(container);
}
In my webconfig i have the following :-
<configSections>
<section name ="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/>
</configSections>
<log4net>
<appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
<file type="log4net.Util.PatternString" value="..\\..\\logs\\WebApi2Book.Api.Log"/>
<appendToFile value="true"/>
<maxSizeRollBackups value="-1"/>
<countDirection value="1"/>
<maximumFileSize value="5MB"/>
<rollingStyle value="Composite"/>
<preserveLogFileNameExtension value="true"/>
<staticLogFileName value="false"/>
<lockingModel type="log4net.Appender.FileAppender+MinimalLock"/>
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date %-5level [%thread] %logger - %message%newline%exception"/>
</layout>
<logger name="NHibernate">
<level value="ERROR"/>
</logger>
<logger name="NHibernate.SQL">
<level value="ERROR"/>
</logger>
<root>
<level value="ALL"/>
<appender-ref ref="LogFileAppender"/>
</root>
</appender>
</log4net>
In the Global.asax class, Im testing the logger is actually being injected correctly and then testing logging an error in the Application_Start() method.
protected void Application_Start()
{
var log = WebContainerManager.Get<ILogManager>().GetLog(typeof(WebApiApplication));
log.Error("Yey!!!! works!");
log.Debug("Yey!!!! works!! Debug");
}
I cant find any problems when i debug the program, i've checked that IsDebugEnabled is set as TRUE, and the logger in injected correctly as expected.
The only issue is that the log file isn't generated.
Your problem is that in your root logger you are referencing an appender called "LogFileAppender" but in fact your appender is called "RollingFileAppender" - the appender name must be the same on both places:
<appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
…
<root>
<level value="ALL"/>
<appender-ref ref="RollingFileAppender "/>
</root>

Does the Log4net SMTPAppender send email Asynchronously?

Does the Log4net SMTPAppender send email asynchronously? If it doesn't, how can I send logging emails asynchronously?
My log4net.config is:
<configuration>
<configSections>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,log4net" />
</configSections>
<log4net>
<appender name="SMTPAppender" type="log4net.Appender.SMTPAppender">
<authentication value="Basic" />
<to value="xxx#xx.com" />
<from value="yyy#xx.com" />
<username value="yyy#xx.com" />
<password value="yyy" />
<subject value="xxx" />
<smtpHost value="smtp.xx.com" />
<lossy value="true" />
<evaluator type="log4net.Core.LevelEvaluator">
<threshold value="WARN" />
</evaluator>
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%newline%date [%thread] %-5level %logger %newline %message%newline%newline%newline" />
</layout>
</appender>
<root>
<level value="INFO"></level>
</root>
<logger name="MyLogger">
<level value="INFO"></level>
<appender-ref ref="SMTPAppender"></appender-ref>
</logger>
</log4net>
</configuration>
You could just call the logging method asynchronously like this:
Task.Factory.StartNew(() => log.Info("Message I want to email"));
I actually got this suggestion from the following SO article:
How do I create an asynchronous wrapper for log4net?
There is a simple workaround before the solid solution is released.
public class SmtpAsyncAppender : SmtpAppender
{
protected override void SendEmail(string messageBody)
{
Task.Run(() => base.SendEmail(messageBody));
}
}
It presumes that SmtpAppender.SendEmail is thread-safe and reenterable. It is for log4net v1.2.13.0 and there is no reason not to be in the future.
Log4net has not built in appender that is asynchronous. If you need that functionality you need to write your own appender. Downloading the log4net source code should get you started...

Can't get Log4Net to work in my ASP.NET website :(

really simple question -> i can't seem to get any data from Log4Net in my ASP.NET application. I've got a simple ASP.NET website, which references a class library. In this class library, I have some lines that call the logger.
I'm trying to read the log4net output data in my Visual Studio 2008 debugging Output window.
Here's my code and my configuration...
//Class Library project
//File: Foo.cs
public class FooService
{
private static readonly ILog log = LogManager.GetLogger(typeof(FooService));
public FooService()
{
// NOTE: To play with my L4N settings, I'll call Debug once, then Info once.
log.Info("Starting Constructor");
// ... snip ...
log.Debug("Leaving Constructor");
}
}
// ASP.NET Website project
// File: global.asax
void Application_Start(object sender, EventArgs eventArgs)
{
log4net.Config.XmlConfigurator.Configure();
}
// File: Whatever.aspx.cs
// A delegate method (when a user clicks a button) creates the FooService() instance.
// File: web.config
<configSections>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler" requirePermission="false" />
// ....
</configSections>
<log4net>
<appender name="ColoredConsoleAppender" type="log4net.Appender.ColoredConsoleAppender">
<mapping>
<level value="ERROR" />
<foreColor value="White" />
<backColor value="Red, HighIntensity" />
</mapping>
<mapping>
<level value="DEBUG" />
<backColor value="Green" />
</mapping>
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %-5level %logger [%property{NDC}] - %message%newline" />
</layout>
</appender>
<appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender">
<layout type="log4net.Layout.PatternLayout" value="%date [%thread] %-5level %logger - %message%newline" />
</appender>
<appender name="OutputDebugStringAppender" type="log4net.Appender.OutputDebugStringAppender">
<mapping>
<level value="ERROR" />
<foreColor value="White" />
<backColor value="Red, HighIntensity" />
</mapping>
<mapping>
<level value="DEBUG" />
<backColor value="Blue" />
</mapping>
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %-5level %logger [%property{NDC}] - %message%newline" />
</layout>
</appender>
<appender name="RollingLogFileAppender" type="log4net.Appender.RollingFileAppender">
<file value="App_Data\logging\log-append.txt"/>
</appender>
<!-- Setup the root category, add the appenders and set the default level -->
<root>
<level value="ALL" />
<appender-ref ref="OutputDebugStringAppender" />
<appender-ref ref="ConsoleAppender" />
<appender-ref ref="ColoredConsoleAppender" />
</root>
<!-- Specify the level for some specific categories -->
<logger name="DotNetOpenAuth">
<level value="ALL" />
</logger>
</log4net>
Cheers for any help or suggestions...
EDIT: Added the RollingLogFileAppender.
I had this same problem and I think it was looking at the wrong web.config or something. I finally separated out log4net.config from web.config and put a path to it \inetpub\Logs\log4net.config and all is well.
UDPATED ON REQUEST: edited from a slightly more complicated version.
<?xml version="1.0" encoding="utf-8"?>
<log4net>
<appender name="TraceAppender" type="log4net.Appender.TraceAppender">
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %-5level (%logger:%line) - %message%newline" />
</layout>
</appender>\
<root>
<!--ALL, DEBUG, INFO, WARN, ERROR, FATAL, OFF-->
<level value="ALL" />
<appender-ref ref="TraceAppender" />
</root>
</log4net>
And it is configured in code as follows:
var logpath = WebConfigurationManager.AppSettings["LogConfigPath"] ?? #"\Inetpub\Logs\log4net.config";
var finfo = new System.IO.FileInfo ( logpath );
XmlConfigurator.Configure( finfo );
ASP.Net has restriction on usage of filesystem access, so try to explicitly point directory under App_Data (were it is allowed)
Here my working sample:
<log4net>
<appender name="FileAppender" type="log4net.Appender.FileAppender">
<file value="App_Data\logging\log-file.txt"/>
Or with rollover
<appender name="RollingLogFileAppender" type="log4net.Appender.RollingFileAppender">
<file value="App_Data\logging\log-append.txt"/>
Ok, found the answer. I needed to use a TraceAppender.
The application configuration file
can be used to control what listeners
are actually used. See the MSDN
documentation for the Trace class for
details on configuring the trace
system.
Events are written using the
System.Diagnostics.Trace.Write(string,string)
method. The event's logger name is
passed as the value for the category
name to the Write method.
Here's my config file data...
<log4net>
<appender name="TraceAppender" type="log4net.Appender.TraceAppender">
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %-5level %logger [%property{NDC}] - %message%newline" />
</layout>
</appender>
<!-- Setup the root category, add the appenders and set the default level -->
<root>
<level value="ALL" />
<appender-ref ref="TraceAppender" />
</root>
</log4net>
There are atleast 2 possible problems:
The way the address to the file is specified, try using an absolute path instead.
The process making the call needs rights to modify the file. Check that the user account making writing to the log file has rights to do so.
To debug it I would create and empty directory, give everyone full control, configure to log to that directory. Then test it, see that it works, then gradually tighten the security to an acceptable level.
I added the following line Global.asax file, and it works..!!
log4net.Config.XmlConfigurator.Configure();

Resources