I am using a custom security expression handler and using spring 3.2.0. Here is the custom expression root class :
public class CustomerPortalSecurityExpressionRoot extends WebSecurityExpressionRoot {
private static final Log logger = LogFactory.getLog(CustomerPortalSecurityExpressionRoot.class);
private CustomerPortalPanicService customerPortalPanicService;
public CustomerPortalSecurityExpressionRoot(Authentication a, FilterInvocation fi) {
super(a, fi);
}
public boolean isPanicking() {
if (customerPortalPanicService != null) {
return customerPortalPanicService.isPanicking();
} else {
logger.warn("CustomerPortalPanicService is not available.");
return false;
}
}
public boolean hasGotPermission(String title){
logger.debug("coming inside has Permission! #public class CustomerPortalSecurityExpressionRoot "+title);
return true;
}
public void setCustomerPortalPanicService(CustomerPortalPanicService customerPortalPanicService) {
this.customerPortalPanicService = customerPortalPanicService;
}
}
I am using it this way in a spring security config file :
<http auto-config="true" use-expressions="true" >
<form-login login-page="/login" login-processing-url="/loginIFM" authentication-failure-url="/login/?login_error=1" username-parameter="username" password-parameter="password" />
<logout invalidate-session="true" logout-success-url="/" logout-url="/logout_ifm" />
<expression-handler ref="webSecurityExpressionHandler"/>
<!-- Rules. -->
<!-- <intercept-url pattern="/" access="permitAll" /> -->
<intercept-url pattern="/hardcopy/*" access="isAuthenticated() and hasPermission('tw')" />
</http>
<!-- expression custom handler -->
<b:bean id="webSecurityExpressionHandler" class="no.user.security.DnWebSecurityExpressionHandler" />
The authentication is taking place using authentication manager, I just want to know that how could I get hold of that user details which is coming as a JSON response after authentication? I know that there is a hasPermission thing in PermissionEvaluator, but this is much more flexible for me. Help!
You can use SecurityContextHolder.getContext().getAuthentication().getAuthorities() to get a hold of the authorities granted to the currently authenticated user.
Related
I've read and applied the Creating a Custom Login tutorial for custom login page and also custom authentication provider to my project.
As far as I understand from the documentations, spring security handles the login error by putting a parameter to the url such as "login?error=blabla".
But in my situation, no matter what user enters except for the true credentials, no request parameters are shown. But normal logins (meaning true logins) works fine.
Is there something I miss ?
CustomAuthenticationProviderImpl.java
#Component
public class CustomAuthenticationProviderImpl implements UnalAuthenticationProvider {
#Autowired
private CustomUserDetailService customUserDetailService;
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String username = authentication.getName();
String password = (String) authentication.getCredentials();
UserDetails user = null;
try{
user = (UserDetails) customUserDetailService.loadUserByUsername(username);
}
catch(UsernameNotFoundException ex){
System.out.println("User name not found");
throw ex;
}
if (user == null) {
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;
}
}
spring-security-config.xml
<global-method-security pre-post-annotations="enabled" />
<http auto-config="true">
<intercept-url pattern="/resources/**" access="permitAll" />
<intercept-url pattern="/login" access="permitAll" />
<intercept-url pattern="/access-denied" access="permitAll" />
<intercept-url pattern="/admin**" access="hasRole('ADMIN')" />
<intercept-url pattern="/**" access="hasRole('USER')" />
<form-login login-page="/login" default-target-url="/main" always-use-default-target="true"/>
<logout logout-url="/logout" logout-success-url="/"/>
<headers>
<frame-options policy="SAMEORIGIN"/>
</headers>
<session-management>
<concurrency-control expired-url="/login" />
</session-management>
</http>
<authentication-manager alias="authenticationManager">
<authentication-provider ref="customAuthenticationProviderImpl" />
</authentication-manager>
After giving some thought, I also decided to remove all intercept-url, thinking that might be a unintented redirection because of those... But it also didn't work.
At a first glance you miss a configuration parameter in your <form-login> section, namely the authentication-failure-url.
This is where you instruct Spring Security to redirect your failing login attempts.
Try to add it as follows:
<form-login login-page="/login" <!--other configurations--> authentication-failure-url="/login?error=true" />.
And, of course, take care of managing the error parameter in your custom login jsp.
After a research, I still dont found a solution for this problem.
My aim is to validate user in Custom Authentication Provider using database but the #Autowiring always throw Null Pointer Exception.
Here my code:
Custom Authentication Provider:
#Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
#Autowired
private Validator iValidate;
#Override
public Authentication authenticate(Authentication auth) throws AuthenticationException{
if (iValidate.userNameCheck(auth.getName()) != "00") {
auth=null;
}
return auth:
}
#Override
public boolean supports(Class<?> auth) {
return (UsernamePasswordAuthenticationToken.class.isAssignableFrom(auth));
}
}
Validator.java:
#Component
public class Validator implements IsamSvc {
private StopWatch sw = new StopWatch();
#Autowired
private JdbcTemplate jdbcTemplate;
#Override
public String userNameCheck(String mercid, String caller) {
/////validating code/////
}
}
Spring Security XML:
<global-method-security pre-post-annotations="enabled" />
<http pattern="/resources/**" security="none" />
<http auto-config="false" use-expressions="true" disable-url-rewriting="true" >
<session-management session-fixation-protection="newSession" session-authentication-error-url="/login.do?sessionalreadyexist">
<concurrency-control max-sessions="1" expired-url="/login.do?expired" error-if-maximum-exceeded="true" />
</session-management>
<intercept-url pattern="/clearcache" access="permitAll()" />
<intercept-url pattern="/login.do" access="permitAll()" />
<intercept-url pattern="/**" access="isFullyAuthenticated()" />
<logout logout-success-url="/login.do?logout" delete-cookies="JSESSIONID" invalidate-session="true" />
<access-denied-handler error-page="/403" />
<form-login login-page='/login.do' login-processing-url="/login" default-target-url="/main" always-use-default-target="true" authentication-failure-url="/login.do?error" username-parameter="username" password-parameter="password" authentication-details-source-ref="CustomAuthInfoSource" /> -->
</http>
<authentication-manager>
<authentication-provider ref="CustomAuthenticationProvider" />
</authentication-manager>
<beans:bean id="CustomAuthenticationProvider" class="xx.xxx.xxxx.xxxxx.CustomAuthenticationProvider" />
beans.xml:
<beans:bean id="iValidate" class="xx.xxx.xxxx.xxxxx.Validator" scope="prototype" />
/////// Other beans ////////
when i call #Autowired private Validator iValidate; in #Controller class, it work normally,but in CustomAuthenticationProvider, it will return null ...
Any Solution?
annotate the CustomAuthenticationProvider with #Component annotation
Here is my spring security config:
<http pattern="/auth/login" security="none" />
<http pattern="/auth/loginFailed" security="none" />
<http pattern="/resources/**" security="none" />
<http auto-config="true" access-decision-manager-ref="accessDecisionManager">
<intercept-url pattern="/auth/logout" access="permitAll"/>
<intercept-url pattern="/admin/**" access="ADMINISTRATIVE_ACCESS"/>
<intercept-url pattern="/**" access="XYZ_ACCESS"/>
<form-login
login-page="/auth/login"
authentication-failure-url="/auth/loginFailed"
authentication-success-handler-ref="authenticationSuccessHandler" />
<logout logout-url="/auth/logout" logout-success-url="/auth/login" />
</http>
The authenticationSuccessHandler extends the SavedRequestAwareAuthenticationSuccessHandler ensuring that the user is redirected to the page he originally requested.
However, since /auth/login is marked as security="none", I am unable to successfully redirect the user to the homepage if he accesses the login page after being logged in. I believe this is the right user experience too.
I tried the below too but the Principal object is always null, presumably because of the security="none" attribute again.
#RequestMapping(value = "/auth/login", method = GET)
public String showLoginForm(HttpServletRequest request, Principal principal) {
if(principal != null) {
return "redirect:/";
}
return "login";
}
I've checked the topic more deeply than last time and found that you have to determine if user is authenticated by yourself in controller. Row Winch (Spring Security dev) says here:
Spring Security is not aware of the internals of your application
(i.e. if you want to make your login page flex based upon if the user
is logged in or not). To show your home page when the login page is
requested and the user is logged in use the SecurityContextHolder in
the login page (or its controller) and redirect or forward the user to
the home page.
So solution would be determining if user requesting /auth/login is anonymous or not, something like below.
applicationContext-security.xml:
<http auto-config="true" use-expressions="true"
access-decision-manager-ref="accessDecisionManager">
<intercept-url pattern="/auth/login" access="permitAll" />
<intercept-url pattern="/auth/logout" access="permitAll" />
<intercept-url pattern="/admin/**" access="ADMINISTRATIVE_ACCESS" />
<intercept-url pattern="/**" access="XYZ_ACCESS" />
<form-login login-page="/auth/login"
authentication-failure-url="/auth/loginFailed"
authentication-success-handler-ref="authenticationSuccessHandler" />
<logout logout-url="/auth/logout" logout-success-url="/auth/login" />
</http>
<beans:bean id="defaultTargetUrl" class="java.lang.String">
<beans:constructor-arg value="/content" />
</beans:bean>
<beans:bean id="authenticationTrustResolver"
class="org.springframework.security.authentication.AuthenticationTrustResolverImpl" />
<beans:bean id="authenticationSuccessHandler"
class="com.example.spring.security.MyAuthenticationSuccessHandler">
<beans:property name="defaultTargetUrl" ref="defaultTargetUrl" />
</beans:bean>
Add to applicationContext.xml bean definition:
<bean id="securityContextAccessor"
class="com.example.spring.security.SecurityContextAccessorImpl" />
which is class
public final class SecurityContextAccessorImpl
implements SecurityContextAccessor {
#Autowired
private AuthenticationTrustResolver authenticationTrustResolver;
#Override
public boolean isCurrentAuthenticationAnonymous() {
final Authentication authentication =
SecurityContextHolder.getContext().getAuthentication();
return authenticationTrustResolver.isAnonymous(authentication);
}
}
implementing simple interface
public interface SecurityContextAccessor {
boolean isCurrentAuthenticationAnonymous();
}
(SecurityContextHolder accessing code is decoupled from controller, I followed suggestion from this answer, hence SecurityContextAccessor interface.)
And last but not least redirect logic in controller:
#Controller
#RequestMapping("/auth")
public class AuthController {
#Autowired
SecurityContextAccessor securityContextAccessor;
#Autowired
#Qualifier("defaultTargetUrl")
private String defaultTargetUrl;
#RequestMapping(value = "/login", method = RequestMethod.GET)
public String login() {
if (securityContextAccessor.isCurrentAuthenticationAnonymous()) {
return "login";
} else {
return "redirect:" + defaultTargetUrl;
}
}
}
Defining defaultTargetUrl String bean seems like a hack, but I don't have better way not to hardcode url... (Actually in our project we use <util:constant> with class containing static final String fields.) But it works after all.
You could also restrict your login page to ROLE_ANONYMOUS and set an <access-denied-handler />:
<access-denied-handler ref="accessDeniedHandler" />
<intercept-url pattern="/auth/login" access="ROLE_ANONYMOUS" />
And in your handler check if the user is already authenticated:
#Service
public class AccessDeniedHandler extends AccessDeniedHandlerImpl {
private final String HOME_PAGE = "/index.html";
#Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException e) throws IOException, ServletException {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth != null && !(auth instanceof AnonymousAuthenticationToken)) {
response.sendRedirect(HOME_PAGE);
}
super.handle(request, response, e);
}
}
Implement a Redirect Interceptor for this purpose:
The Interceptor (implementing HandlerInterceptor interface) check if someone try to access the login page, and if this person is already logged in, then the interceptor sends a redirect to the index page.
public class LoginPageRedirectInterceptor extends HandlerInterceptorAdapter {
private String[] loginPagePrefixes = new String[] { "/login" };
private String redirectUrl = "/index.html";
private UrlPathHelper urlPathHelper = new UrlPathHelper();
#Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
if (isInLoginPaths(this.urlPathHelper.getLookupPathForRequest(request))
&& isAuthenticated()) {
response.setContentType("text/plain");
sendRedirect(request, response);
return false;
} else {
return true;
}
}
private boolean isAuthenticated() {
Authentication authentication =
SecurityContextHolder.getContext().getAuthentication();
if (authentication == null) {
return false;
}
if (authentication instanceof AnonymousAuthenticationToken) {
return false;
}
return authentication.isAuthenticated();
}
private void sendRedirect(HttpServletRequest request,
HttpServletResponse response) {
String encodedRedirectURL = response.encodeRedirectURL(
request.getContextPath() + this.redirectUrl);
response.setStatus(HttpStatus.SC_TEMPORARY_REDIRECT);
response.setHeader("Location", encodedRedirectURL);
}
private boolean isInLoginPaths(final String requestUrl) {
for (String login : this.loginPagePrefixes) {
if (requestUrl.startsWith(login)) {
return true;
}
}
return false;
}
}
You can keep it simple flow by access-denied-page attribute in http element or as dtrunk said to write handler for access denied as well as. the config would be like
<http access-denied-page="/403" ... >
<intercept-url pattern="/login" access="ROLE_ANONYMOUS" />
<intercept-url pattern="/user/**" access="ROLE_USER" />
<intercept-url pattern="/admin/**" access="ROLE_ADMIN" />
<form-login login-page="/login" default-target-url="/home" ... />
...
</http>
in controller for /403
#RequestMapping(value = "/403", method = RequestMethod.GET)
public String accessDenied() { //simple impl
return "redirect:/home";
}
and for /home
#RequestMapping(value = "/home", method = RequestMethod.GET)
public String home(Authentication authentication) {
// map as many home urls with Role
Map<String, String> dashBoardUrls = new HashMap<String, String>();
dashBoardUrls.put("ROLE_USER", "/user/dashboard");
dashBoardUrls.put("ROLE_ADMIN", "/admin/dashboard");
String url = null;
Collection<? extends GrantedAuthority> grants = authentication
.getAuthorities();
// for one role per user
for (GrantedAuthority grantedAuthority : grants) {
url = dashBoardUrls.get(grantedAuthority.getAuthority());
}
if (url == null)
return "/errors/default_access_denied.jsp";
return "redirect:" + url;
}
and when you make request for /admin/dashboard without logged in, it will redirect /login automatically by security
<http pattern="/login" auto-config="true" disable-url-rewriting="true">
<intercept-url pattern="/login" access="ROLE_ANONYMOUS"/>
<access-denied-handler error-page="/index.jsp"/>
</http>
You can try checking
if(SecurityContextHolder.getContext().getAuthentication() == null)
True means the user isn't authenticated, and thus can be sent to the login page. I don't know how robust/reliable this is, but it seems reasonable to try.
I am trying to implement a custom session timeout handler using spring security to add a dynamic parameter onto the redirect url and I have a problem where I am getting into an infinite loop but I don't know why. I was wondering if someone could enlighten me on this?
<http use-expressions="true" auto-config="false" entry-point-ref="loginUrlAuthenticationEntryPoint">
<!-- custom filters -->
<custom-filter position="FORM_LOGIN_FILTER" ref="twoFactorAuthenticationFilter" />
<custom-filter after="SECURITY_CONTEXT_FILTER" ref="securityLoggingFilter"/>
<custom-filter before="SESSION_MANAGEMENT_FILTER" ref="sessionManagementFilter" />
<!-- session management -->
<session-management session-fixation-protection="none" />
<!-- error handlers -->
<access-denied-handler error-page="/accessDenied.htm"/>
<!-- logout -->
<logout
invalidate-session="false"
delete-cookies="JSESSIONID"
success-handler-ref="customUrlLogoutSuccessHandler"/>
<!-- authorize pages -->
<intercept-url pattern="/home.htm" access="isAuthenticated()" />
<intercept-url pattern="/shortsAndOvers.htm" access="isAuthenticated()" />
<intercept-url pattern="/shortsAndOversDaily.htm" access="isAuthenticated()" />
<intercept-url pattern="/birtpage.htm" access="isAuthenticated()" />
<intercept-url pattern="/reports/show.htm" access="isAuthenticated()" />
</http>
<beans:bean id="sessionManagementFilter" class="org.springframework.security.web.session.SessionManagementFilter">
<beans:constructor-arg name="securityContextRepository" ref="httpSessionSecurityContextRepository" />
<beans:property name="invalidSessionStrategy" ref="customSimpleRedirectInvalidSessionStrategy" />
</beans:bean>
<beans:bean id="customSimpleRedirectInvalidSessionStrategy" class="com.myer.reporting.security.CustomSimpleRedirectInvalidSessionStrategy">
<beans:constructor-arg name="invalidSessionUrl" value="/sessionExpired.htm" />
<beans:property name="createNewSession" value="false" />
</beans:bean>
<beans:bean id="httpSessionSecurityContextRepository" class="org.springframework.security.web.context.HttpSessionSecurityContextRepository"/>
So far so good. When I get a session timeout the code in CustomSimpleRedirectInvalidSessionStrategy.onInvalidSessionDetected is called....
public CustomSimpleRedirectInvalidSessionStrategy(String invalidSessionUrl) {
Assert.isTrue(UrlUtils.isValidRedirectUrl(invalidSessionUrl), "url must start with '/' or with 'http(s)'");
this.destinationUrl = invalidSessionUrl;
}
public void onInvalidSessionDetected(HttpServletRequest request, HttpServletResponse response) throws IOException {
logger.debug("Starting new session (if required) and redirecting to '" + destinationUrl + "'");
SecurityContext securityContext = SecurityContextHolder.getContext();
String store = null;
ReportingManagerUser user = null;
if (securityContext != null){
Authentication authentication = securityContext.getAuthentication();
if (authentication!=null && !(authentication instanceof AnonymousAuthenticationToken)){
user = (ReportingManagerUser)authentication.getPrincipal();
if (user!=null){
store = user.getStore();
}
}
}
String amendedTargetUrl = null;
if (user !=null && user.isLoggedInWithSiteId()){
amendedTargetUrl =
destinationUrl.concat(
ParamConstants.PARAM_PREFIX +
ParamConstants.PARAM_SITE_ID +
ParamConstants.PARAM_EQ
+ store);
}else{
amendedTargetUrl = destinationUrl;
}
if (createNewSession) {
request.getSession();
}
redirectStrategy.sendRedirect(request, response, amendedTargetUrl);
}
And the code is executed properly but even after the redirect the code just keeps falling into the onInvalidSessionDetected method instead of actually redirecting to what I've configured in the invalidSessionUrl property.
I don't really get it.
thanks in advance
Silly mistake. I did not have security excluded on the redirect url. So it was trying to validation the jsession for that page and failing each time. Lol
I am using Spring and Ldap for user authentication , Now I want to redirect the user to homepage when user is already logged in , I tried few solutions that I read through google but no luck. Here is my configuration code ...
Spring-Security.xml
<security:http auto-config="true" use-expressions="true"
access-denied-page="/denied" access-decision-manager-ref="accessDecisionManager"
disable-url-rewriting="true">
<security:remember-me key="_spring_security_remember_me"
token-validity-seconds="864000" token-repository-ref="tokenRepository" />
<security:intercept-url pattern="/login/login"
access="permitAll" />
<security:intercept-url pattern="/resources/**"
access="permitAll" />
<security:intercept-url pattern="/member/*"
access="permitAll" />
<security:intercept-url pattern="user/admin/admin"
access="hasRole('ROLE_ADMIN')" />
<security:intercept-url pattern="/user/user"
access="hasRole('ROLE_USERS')" />
<security:form-login login-page="/login/login"
authentication-failure-url="/login/login?error=true"
default-target-url="/checking" />
<security:logout invalidate-session="true"
logout-success-url="/login/login" logout-url="/login/logout" />
<security:custom-filter ref="captchaCaptureFilter"
before="FORM_LOGIN_FILTER" />
<!-- <security:custom-filter ref="captchaVerifierFilter" after="FORM_LOGIN_FILTER"
/> -->
<!-- <security:session-management
invalid-session-url="/logout.html">
<security:concurrency-control
max-sessions="1" error-if-maximum-exceeded="true" />
</security:session-management>
<security:session-management
session-authentication-strategy-ref="sas" /> -->
</security:http>
My Login Controller
public class LoginController {
#Autowired
private User user;
#Autowired
SecurityContextAccessor securityContextAccessor;
#RequestMapping(value = "login/login", method = RequestMethod.GET)
public String getLogindata(
#RequestParam(value = "error", required = false) String error,
Model model) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
model.addAttribute("error", error);
if (securityContextAccessor.isCurrentAuthenticationAnonymous() && auth.getPrincipal() == null) {
return "login/login";
} else {
return "redirect:/member/user";
}
}
}
auth.getName() always gives me anonymous user even if I am already logged in....
You should write your target redirection to the home page,
after successfully login in to the default-target-url in spring-security.xml, instead of default-target-url="/checking" .
I am not sure what are you trying to achieve in /login/login Controller ?
If you only want to redirect user, after successfully login to the /member/user , you should write:
<security:form-login login-page="/login"
always-use-default-target='true'
default-target-url="/member/user"
authentication-failure-url="/login/login?error=true"
/>
And login Controller should look like this:
#RequestMapping(value="/login", method = RequestMethod.GET)
public String login(ModelMap model) {
return "login";
}
If your authentication provider is OK and correct , Spring security will redirect you automatically to the /member/user after successfully login.