I've changed the default Authentication Provider for a Custom one.
This is my AuthenticationProvider
public class CustomAuthenticationProvider implements AuthenticationProvider {
#Autowired
private ParamsProperties paramsProperties;
#SuppressWarnings("unchecked")
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
//Check username and passwd
String user = (String) authentication.getPrincipal();
String pass = (String) authentication.getCredentials();
if(StringUtils.isBlank(user) || StringUtils.isBlank(pass) ){
throw new BadCredentialsException("Incorrect username/password");
}
//Create SSO
SingleSignOnService service = new SingleSignOnService(paramsProperties.getServicesServer());
try {
//Check logged
service.setUsername(authentication.getName());
service.setPassword(authentication.getCredentials().toString());
ClientResponse response = service.call();
String result = response.getEntity(String.class);
ObjectMapper mapper = new ObjectMapper();
Map<String,Object> map = mapper.readValue(result, new TypeReference<Map<String,Object>>() {} );
//Read code
String code = (String)map.get("code");
log.debug(" ** [Authenticate] Result: " + code );
for (String s : (List<String>)map.get( "messages" ) ) {
log.debug(" [Authenticate] Message: " + s );
}
if ( code.equals( "SESSION_CREATED" ) || code.equals( "SESSION_UPDATED" ) || code.equals( "SESSION_VERIFIED" ) ) {
UsernamePasswordAuthenticationToken tokenSSO = LoginHelper.getuserSringTokenFromAuthService(map);
return tokenSSO;
} else {
return null;
}
} catch (Exception e) {
e.printStackTrace();
throw new AuthenticationServiceException( e.getMessage() );
}
}
public boolean supports(Class authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
And this is my security.xml
<http>
<form-login default-target-url ="/Login.html" always-use-default-target="true" login-page="/Login.html" login-processing-url="/j_spring_security_check"
authentication-failure-url="/Login.html" />
<http-basic />
<logout logout-success-url="/Login.html" />
</http>
<beans:bean id="localeFilter" class="com.mycomp.comunes.server.spring.controller.login.MyLocaleFilter" lazy-init="true">
<custom-filter position="LAST"/>
</beans:bean>
<beans:bean id="authenticationProvider" class="com.indra.rfef.comunes.server.spring.manager.autenticacion.CustomAuthenticationProvider">
<custom-authentication-provider />
</beans:bean>
It gets over my CustomAuthenticationProvider, and authenticates correctly the user. But when returning tokenSSO, of type UsernamePasswordAuthenticationToken, it seems it's not saving the user on the Security Context, and when I redirect the user (on the callback of the authenticate) to the index.html, I get redirected back to Login.html.
Why could this happen? I'm I forgetting something?
Please fix your configuration:
<http>
<intercept-url pattern="/Login*" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
<intercept-url pattern="/**" access="ROLE_USER"/>
<form-login login-page="/Login.html" login-processing-url="/j_spring_security_check" authentication-failure-url="/Login.html" />
<http-basic />
<logout logout-success-url="/Login.html" />
</http>
Remove default-target-url ="/Login.html". It makes the redirection after login to the same login page. The default is /.
Add security on all URLs <intercept-url pattern="/**" access="ROLE_USER"/>
Do not remove the anonymous access from the login page
Why you need BasicAuthentication? Remove it if not required: <http-basic />
Related
I have a problem. Suppose I login into the application and accessing different pages and leaving the application ideal for 5 min in http://localhost:8080/InformationManagement/smartapp/allFileNetStatus and then trying to access once the session get expired and get redirected to login page.
Once I enter the credentials it get logged in it get me to http://localhost:8080/InformationManagement/smartapp/allFileNetStatus instead of home page(http://localhost:8080/InformationManagement/)
Note: My Login page and Home page URL is same
How can I control this in spring security.
Code:
<http pattern="/resources" security="none" />
<http auto-config="true" use-expressions="true">
<intercept-url pattern="/login" access="permitAll" />
<intercept-url pattern="/logout" access="permitAll" />
<intercept-url pattern="/denied" access="hasRole('ROLE_USER')" />
<intercept-url pattern="/" access="permitAll" />
<intercept-url pattern="/user" access="hasRole('ROLE_USER')" />
<intercept-url pattern="/user/create" access="hasRole('ROLE_ADMIN')" />
<intercept-url pattern="/user/update"
access="hasAnyRole('ROLE_READ','ROLE_ADMIN')" />
<intercept-url pattern="/smartapp/getNewFileNetStatus" access="hasRole('ROLE_SMARTAPP')" />
<intercept-url pattern="/smartapp/allFileNetStatus" access="hasRole('ROLE_SMARTAPP')" />
<intercept-url pattern="/user/alluser" access="hasAnyRole('ROLE_READ','ROLE_ADMIN')" />
<intercept-url pattern="/admin" access="hasRole('ROLE_ADMIN')" />
<form-login login-page="/login" authentication-failure-url="/login/failure"
default-target-url="/" />
<access-denied-handler error-page="/denied" />
<logout invalidate-session="true" logout-success-url="/logout/success"
logout-url="/logout" />
</http>
<beans:bean id="daoAuthenticationProvider"
class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
<beans:property name="userDetailsService" ref="userDetailsService"></beans:property>
</beans:bean>
<beans:bean id="authenticationManager"
class="org.springframework.security.authentication.ProviderManager">
<beans:property name="providers">
<beans:list>
<beans:ref local="daoAuthenticationProvider" />
</beans:list>
</beans:property>
</beans:bean>
<authentication-manager>
<authentication-provider user-service-ref="userDetailsService">
<password-encoder hash="md5"></password-encoder>
</authentication-provider>
</authentication-manager>
HomeController.java
#Controller
#RequestMapping("/")
public class HomeController {
/*
* #Value("${msg}") private String msg;
*/
#Autowired
UserDetailsService userService;
Logger logger = Logger.getLogger(HomeController.class);
#RequestMapping(value = "/help", method = RequestMethod.GET)
public String getAdminPage() {
return "help";
}
#RequestMapping(method = RequestMethod.GET)
public String getHomePage(Model model, HttpSession session) {
Authentication auth = SecurityContextHolder.getContext()
.getAuthentication();
if (!(auth instanceof AnonymousAuthenticationToken)) {
/* The user is logged in :) */
if (logger.isInfoEnabled()) {
logger.info("User got logged in...");
}
int passwordResetValue = userService.userPasswordReset(auth
.getName());
session.setAttribute("username",auth.getName());
System.out.println("username-- set-->"+session.getAttribute("username"));
System.out.println("passwordResetValue" + passwordResetValue);
if (passwordResetValue == 0) {
return "home";
} else {
return "redirect:/password/changePassword?value=reset";
}
} else {
if (logger.isInfoEnabled()) {
logger.info("Redirected to Login Page");
}
return "access/login";
}
}
AccessController.java
#Controller
#RequestMapping
public class AccessController {
#RequestMapping(value = "/denied")
public String denied() {
return "access/denied";
}
#RequestMapping("/login")
public String login() {
/*System.out.println("message-->" + message);
model.addAttribute("message", message);*/
Authentication auth = SecurityContextHolder.getContext()
.getAuthentication();
if (!(auth instanceof AnonymousAuthenticationToken)) {
auth.getPrincipal();
/* The user is logged in :) */
System.out.println("eeee");
return "redirect:/";
} else {
System.out.println("Finalalaay" + auth.getDetails());
return "access/login";
}
}
#RequestMapping(value = "/login/failure")
public String loginFailure(final RedirectAttributes redirect) {
String message = "Please verify username and password";
Authentication auth = SecurityContextHolder.getContext()
.getAuthentication();
if (!(auth instanceof AnonymousAuthenticationToken)) {
/* The user is logged in :) */
return "redirect:/";
} else {
redirect.addFlashAttribute("message", message);
return "redirect:/login";
}
}
#RequestMapping(value = "/logout/success")
public String logoutSuccess(final RedirectAttributes redirect) {
String message = "You have been successfully logged out.";
redirect.addFlashAttribute("message", message);
return "redirect:/login";
}
}
You should implement your own AuthenticationSuccessHandler for that.
<!-- Add to your form login the handler-->
<form-login login-page="/login" authentication-failure-url="/login/failure"
default-target-url="/" authentication-success-handler-ref="homeRedirectSuccessHandler" />
<beans:bean id="homeRedirectSuccessHandler"
class="your.package.HomeRedirectSuccessHandler" />
And in your HomeRedirectSuccessHandler:
protected void handle(HttpServletRequest request,
HttpServletResponse response, Authentication authentication) throws IOException {
redirectStrategy.sendRedirect(request, response, "yourHomepage.html);
}
I am new with RESTful web services in spring,whenever i am requesting the URL through postman,i am getting random generated token from server side, here you are controller code,through this i am getting random generated token.
#RequestMapping(value = "/api/authenticate", method = RequestMethod.POST)
public #ResponseBody
Result doLogIn(#RequestParam("BulkData") String bulkData, HttpServletResponse response) throws Exception {
ObjectMapper mapper = new ObjectMapper();
JsonNode actualObj = null;
try {
actualObj = mapper.readTree(bulkData);
} catch (IOException e1) {
e1.printStackTrace();
return new Result("Invalid Request", ResultCodes.LOGIN_FAILURE);
}
String userName = actualObj.get("userName").asText();
String password = actualObj.get("password").asText();
logger.debug("[REST]: Attempting login for -> " + userName);
UserDetails details = userDetailService.loadUserByUsername(userName);
// validate password
if (details != null && !details.getPassword().equals(password)) {
logger.debug("[REST]: Invalid username/password");
try {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Invalid username/password");
} catch (IOException e) {
e.printStackTrace();
}
return new Result("Invalid username or password", ResultCodes.LOGIN_FAILURE);
}
// Generate token. ATM, use only username
String generatedToken = Jwts.builder().setSubject(userName)
.setIssuedAt(new Date())
// set token expiration time
.setExpiration(new Date(System.currentTimeMillis() + Config.TOKEN_EXPIRY_PERIOD))
.signWith(SignatureAlgorithm.HS256, servletContext.getInitParameter("API_SECRET_KEY"))
.compact();
// provide token to user in form of a Http Header
response.addHeader(Config.AUTH_TOKEN_HEADER_NAME, generatedToken);
return new Result("Login Success", ResultCodes.LOGIN_SUCCESS_TOKEN_GENERATED);
}
and here is the code for authorization , to do so i am using AuthenticationTokenProcessingFilter,
public class AuthenticationTokenProcessingFilter extends GenericFilterBean {
#Autowired
private UserDetailService userDetailService;
#Autowired
private AuthenticationManager authenticationManager;
private static Logger logger = Logger.getLogger(AuthenticationTokenProcessingFilter.class);
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
// Exclude login URL
if(req.getRequestURI().endsWith("/api/authenticate")) {
chain.doFilter(req, response);
return;
}
// Client must send token in header
String authHeader = req.getHeader(Config.AUTH_TOKEN_HEADER_NAME);
if (authHeader == null) {
logger.error("[REST]: Authentication header was null...");
throw new ServletException("Missing or invalid Authorization header.");
}
// Parse token, fetch user and reload Security Context
try {
String SECRET_KEY = getServletContext().getInitParameter("API_SECRET_KEY");
Jws<Claims> claims = Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(authHeader);
Claims claim = claims.getBody();
String userName = claim.getSubject();
logger.debug("[REST]: Token of user -> " + userName + " expires: " + claim.getExpiration());
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(userName, userDetailService.loadUserByUsername(userName).getPassword());
token.setDetails(new WebAuthenticationDetails(req));
Authentication authentication = authenticationManager.authenticate(token);
SecurityContextHolder.getContext().setAuthentication(authentication);
} catch (SignatureException e) {
logger.debug("[REST]: Invalid token");
throw new ServletException("Invalid token.");
}
chain.doFilter(req, response);
// clear security context now because we are going for Stateless Web Services
SecurityContextHolder.getContext().setAuthentication(null);
}
now i want to use this generated token to call this method ,
#RequestMapping(value="/api/admin/getEmployeerole", method=RequestMethod.POST)
public List<EmployeeRole> EmployeeRoleList() {
List<EmployeeRole> getRole=employeeRoleService.getAll();
return getRole;
}
now what is happening here when i am writing this URL to postman and adding generated token into header ,and i have used authorization type (No auth), i have also tried with basic Authorization, still my request is going to customAuthenticationEntrypoint and it throws Access denied exception,that the user Role is annonymous, at server side i am getting status 401 unauthorized.it would be great if someone can help to get over from this..
here You are my spring security configuration..
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<context:component-scan base-package="hp.bootmgr.authentication.provider" />
<http pattern="/resources/**" security="none" />
<http pattern="/api/**" realm="Protected API" use-expressions="true" auto-config="false" create-session="stateless" entry-point-ref="customAuthenticationEntryPoint">
<!-- <custom-filter position="FORM_LOGIN_FILTER" /> -->
<intercept-url pattern="/api/authenticate" access="permitAll()" />
<intercept-url pattern="/api/admin/**" access="hasRole('ADMIN')" />
<intercept-url pattern="/api/user/**" access="hasAnyRole('ADMIN', 'EMPLOYEE')" />
<intercept-url pattern="/api/member/**" access="hasAnyRole('ADMIN', 'MEMBER')" />
<!--<form-login
login-page="/api/authenticate"
login-processing-url="/j_spring_security_check"
username-parameter="userName"
password-parameter="password" />-->
<logout logout-url="/logout"/>
<csrf disabled="true"/>
</http>
<http auto-config="true" use-expressions="true" entry-point-ref="authenticationEntryPoint">
<access-denied-handler error-page="/403" />
<intercept-url pattern="/login" access="true"/>
<intercept-url pattern="/admin/**" access="hasRole('ADMIN')" />
<!-- Allow access to user pages to admin, as long as there is no more other rules-->
<intercept-url pattern="/user/**" access="hasAnyRole('ADMIN', 'EMPLOYEE')" />
<intercept-url pattern="/member/**" access="hasAnyRole('ADMIN', 'MEMBER')" />
<form-login
login-page="/login"
default-target-url="/home"
authentication-failure-url="/login?failed=1"
login-processing-url="/j_spring_security_check"
username-parameter="userName"
password-parameter="password" />
<logout logout-success-url="/login?logout=1" invalidate-session="true" logout-url="/logout"/>
<!-- enable csrf protection -->
<csrf disabled="true"/>
<session-management>
<concurrency-control max-sessions="1" expired-url="/login" />
</session-management>
</http>
<beans:bean id="customAuthenticationEntryPoint" class="hp.bootmgr.web.services.authentication.CustomAuthenticationEntryPoint" />
<beans:bean id="authenticationTokenProcessingFilter" class="hp.bootmgr.web.services.authentication.AuthenticationTokenProcessingFilter" />
<beans:bean id="authenticationEntryPoint" class="hp.bootmgr.security.AuthenticationEntryPoint">
<beans:constructor-arg name="loginUrl" value="/login"/>
</beans:bean>
<authentication-manager alias="authenticationManager">
<authentication-provider user-service-ref="userDetailService" />
</authentication-manager>
</beans:beans>
I want to write my own LDAP authentication provider. I am extending AbstractUserDetailsAuthenticationProvider, which has a method retrieveUser(String username, UsernamePasswordAuthenticationToken authentication).
I want to override this method and write my own data retrieving method. How to do that in Java? How to make an LDAP query and how connect to the LDAP server? I was searching in Internet but I didn't find anything that helped.
EDIT: 22.01.2013
#Override
protected UserDetails retrieveUser(String username,
UsernamePasswordAuthenticationToken authentication)
throws AuthenticationException {
LdapUser userDetail = null;
log.entry("retrieveUser", authentication.getPrincipal());
UsernamePasswordAuthenticationToken userToken = authentication;
String userName = userToken.getName();
userName = userName != null ? userName.toLowerCase() : userName;
String password = userToken.getCredentials().toString();
try {
if (password == null || "".equals(password)) {
log.debug("retrieveUser", "no password provided");
throw new AuthenticationCredentialsNotFoundException(
"Invalid login or password");
}
}
catch (AuthenticationCredentialsNotFoundException e) {
log.debug("retrieveUser", "no password provided");
}
// connection with ldap and check retrieved username and password
connect = connection(userName, password);
if (connect) {
log.debug("retrieve user", "correct connection with ldap");
userDetail = new LdapUser();
setUserDetails(userDetail, ctx, username);
} else {
log.error("retrieve user", "Failed connection");
}
log.exit("retrieveUser", "user logged: " + userDetail);
return userDetail;
}
My security.xml file
<http auto-config='true'>
<intercept-url pattern="/**/*.ico" filters="none" />
<intercept-url pattern="/**/*.gif" filters="none" />
<intercept-url pattern="/**/*.jpg" filters="none" />
<intercept-url pattern="/**/*.css" filters="none" />
<intercept-url pattern="/**/*.js" filters="none" />
<intercept-url pattern="/**/*.png" filters="none" />
<intercept-url pattern="/logout.jsp*" filters="none" />
<intercept-url pattern="/index.jsp*" filters="none" />
<intercept-url pattern="/**" access="ROLE_USER,ROLE_ADMIN" />
<logout logout-success-url="/index.jsp"/>
<form-login login-page="/index.jsp"
authentication-failure-url="/error_ldap.jsp"
default-target-url="/main_ldap.jsp" always-use-default-target="true" />
</http>
<authentication-manager>
<authentication-provider ref="ldapAuthenticationProvider">
<password-encoder hash="sha" />
</authentication-provider>
</authentication-manager>
When login is suceed I got redirect to main_ldap.jsp, but if authentication fail, I got this error. I tried to throw exception UsernameNotFoundException instead returning null in retrieveUser method (which is not allowed) but anything happend (only i got this exception).
You can connect to LDAP from java:
http://docs.oracle.com/javase/jndi/tutorial/ldap/security/ldap.html
but spring security already has ldap integration, you can use of the methods described here:
http://static.springsource.org/spring-security/site/docs/3.0.x/reference/ldap.html
...
xml config for using your own UserDetails service is:
<b:bean id="userDetailsService" class="your.class.here">
</b:bean>
<authentication-provider user-service-ref="userDetailsService">
</authentication-provider>
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'm trying to implement spring security 3.1.0.M1 and I'm unable to get my application to set the Authentication.getPrincipal to my custom UserDetails implementation. It always returns a principal of "guest" when I try to get the logged in user. See getLoggedInUser method below.
In Users.java (UserDetails impl) the getAuthorities method never gets called and maybe that's why the user_role doesn't get assigned.
to Maybe I've misconfigured something...I've attached an outline of my implementation hoping someone can spot my error. Thanks for the assistance!
public static Users getLoggedInUser() {
Users user = null;
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth != null && auth.isAuthenticated()) {
Object principal = auth.getPrincipal();
if (principal instanceof Users) {
user = (Users) principal;
}
}
return user;
}
security context file(removed the xml and schema definitions):
<global-method-security secured-annotations="enabled">
</global-method-security>
<http security="none" pattern="/services/rest-api/1.0/**" />
<http security="none" pattern="/preregistered/**" />
<http access-denied-page="/auth/denied.html">
<intercept-url
pattern="/**/*.xhtml"
access="ROLE_NONE_GETS_ACCESS" />
<intercept-url
pattern="/auth/**"
access="ROLE_ANONYMOUS,ROLE_USER" />
<intercept-url
pattern="/auth/*"
access="ROLE_ANONYMOUS" />
<intercept-url
pattern="/**"
access="ROLE_USER" />
<form-login
login-processing-url="/j_spring_security_check.html"
login-page="/auth/login.html"
default-target-url="/registered/home.html"
authentication-failure-url="/auth/login.html?_dc=45" />
<logout logout-url="/auth/logout.html"
logout-success-url="/" />
<anonymous username="guest" granted-authority="ROLE_ANONYMOUS"/>
<remember-me user-service-ref="userManager" key="valid key here"/>
</http>
<!-- Configure the authentication provider -->
<authentication-manager>
<authentication-provider user-service-ref="userManager">
<password-encoder ref="passwordEncoder" />
</authentication-provider>
</authentication-manager>
UserDetails Implementation (Users.java):
public class Users implements Serializable, UserDetails {
public Collection<GrantedAuthority> getAuthorities() {
List<GrantedAuthority> auth = new ArrayList<GrantedAuthority>();
auth.add(new GrantedAuthorityImpl("ROLE_USER"));
return auth;
}
}
user-service-ref="userManager" (UserManagerImpl.java):
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException {
Users user = null;
try {
user = userDAO.findByUsername(username);
} catch (DataAccessException ex) {
throw new UsernameNotFoundException("Invalid login", ex);
}
if (user == null) {
throw new UsernameNotFoundException("User not found.");
}
return user;
}
Are you not getting compilation error on this line: auth.add("ROLE_USER");?
I think it should be : auth.add(new SimpleGrantedAuthority("ROLE_USER"));