How can I add two security policies in Spring MVC App? - java

I'm writing spring application to serve mobile as well as web portal requests.
I have added Controller to handle web portal requests and RestController to handle mobile requests. This all stuff I have done in single project.
I've configured auth.xml for authetication and all.
<security:http pattern="/api/**" entry-point-ref="restAuthenticationEntryPoint" use-expressions="true" auto-config="false" create-session="stateless" >
<security:intercept-url pattern="/api/auth" access="permitAll" />
<security:intercept-url pattern="/api/token" access="permitAll" />
<security:custom-filter ref="authenticationTokenProcessingFilter" position="FORM_LOGIN_FILTER" />
<security:intercept-url pattern="/api/**" access="isAuthenticated()" />
<security:logout />
</security:http>
<bean class="com.auth.TokenAuthenticationFilter"
id="authenticationTokenProcessingFilter">
<constructor-arg type="java.lang.String"><value>/api/**</value></constructor-arg>
</bean>
<!-- Code for REST API Authentication -->
<!-- create-session="stateless" -->
<security:http auto-config="false" use-expressions="true" entry-point-ref="ajaxAwareAuthenticationEntryPoint" disable-url-rewriting="true">
<security:intercept-url pattern="/login" access="permitAll()" />
<security:intercept-url pattern="/**" access="isAuthenticated()" />
<security:custom-filter position="FORM_LOGIN_FILTER" ref="authenticationFilter" />
<security:custom-filter position="CONCURRENT_SESSION_FILTER" ref="concurrencyFilter" />
<security:logout logout-url="/logout" logout-success-url="/login.do" invalidate-session="true" />
<security:remember-me services-ref="rememberMeService" />
<security:session-management session-authentication-strategy-ref="sas" />
<security:csrf disabled="true"/>
</security:http>
But I want to integrate Spring OAuth 2.0 in it.
Can anyone has idea about the same ?

You can configure 2 different security filters for 2 different paths. That way, you can have differents paths of you application secured differently. Typically, you would want to have "/public/" accessible to anyone while "/api/" being secured by authentication.
I would strongly recommend to configure Spring Security in Java by extending WebSecurityConfigurerAdapter.
Here is an example Java configuration which protects only some endpoints while leaving others accessible publicly.
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled=true)
class SecurityConfig extends WebSecurityConfigurerAdapter {
private static final RequestMatcher PUBLIC_URLS = new OrRequestMatcher(
new AntPathRequestMatcher("/**", OPTIONS.toString()),
new AntPathRequestMatcher("/public/**"),
new AntPathRequestMatcher("/health"),
// Spring Social
new AntPathRequestMatcher("/signin/**"),
new AntPathRequestMatcher("/auth/**"),
// Swagger Documentation
new AntPathRequestMatcher("/swagger-ui.html"),
new AntPathRequestMatcher("/v2/api-docs"),
new AntPathRequestMatcher("/swagger-resources/**"),
new AntPathRequestMatcher("/webjars/**")
);
private static final RequestMatcher PROTECTED_URLS = new NegatedRequestMatcher(PUBLIC_URLS);
#Autowired
private RESTAuthenticationProvider authenticationProvider;
#Autowired
private TokenService credentials;
#Autowired
private UserSecurityService users;
#Override
protected void configure(final AuthenticationManagerBuilder auth) {
auth.authenticationProvider(authenticationProvider);
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
public void configure(final WebSecurity web) throws Exception {
web.ignoring().requestMatchers(PUBLIC_URLS);
}
#Override
protected void configure(final HttpSecurity http) throws Exception {
http
.exceptionHandling()
// this entry point handles when you request a protected page and you are not yet
// authenticated
.defaultAuthenticationEntryPointFor(forbiddenEntryPoint(), PROTECTED_URLS)
.and()
.authenticationProvider(authenticationProvider)
.addFilterBefore(restAuthenticationFilter(), AnonymousAuthenticationFilter.class)
.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.csrf().disable()
.formLogin().disable()
.httpBasic().disable()
.logout().disable()
.sessionManagement().disable();
}
#Bean
RESTAuthenticationFilter restAuthenticationFilter() throws Exception {
final RESTAuthenticationFilter filter =
new RESTAuthenticationFilter(PROTECTED_URLS, credentials);
filter.setAuthenticationManager(authenticationManagerBean());
filter.setAuthenticationSuccessHandler(getSuccessHandler());
return filter;
}
// Upon successful authentication, Spring will attempt to try and move you to another URL
// We have to prevent this because the request for the resource and the authentication all get done in the same request!
#Bean
SimpleUrlAuthenticationSuccessHandler getSuccessHandler() {
final SimpleUrlAuthenticationSuccessHandler successHandler = new SimpleUrlAuthenticationSuccessHandler();
successHandler.setRedirectStrategy(new NoRedirectStrategy());
return successHandler;
}
#Bean
AuthenticationEntryPoint forbiddenEntryPoint() {
return new Http401AuthenticationEntryPoint("Bearer");
}
}

Try out spring security. It has built in functionalities also you can always override existing behavior for your purposes.

Related

Redirect specific requests to login page and throw 401 for others in spring boot application

I have a spring boot application with few restful endpoints. This application also serves the html pages and js files developed using React.
I have implemented user authentication to this application using the spring oauth2. When the user is not authenticated, the requests are redirected to login page, either it be a request to html page or REST endpoint. But I don't want the requests to REST endpoints to return redirect response to login page but just 401.
How can I achieve this?
What you can do is that you define a custom authentication entry point like this.
For every unauthorized requests, they will land in your custom entrypoint.
Here you can add any custom logic to check only what type of REST request you wish to answer with 401.
public class AjaxAwareLoginUrlAuthenticationEntryPoint extends
LoginUrlAuthenticationEntryPoint {
private static final RequestMatcher requestMatcher = new ELRequestMatcher(
"hasHeader('X-Requested-With','XMLHttpRequest')");
#SuppressWarnings("deprecation")
public AjaxAwareLoginUrlAuthenticationEntryPoint() {
super();
}
public AjaxAwareLoginUrlAuthenticationEntryPoint(String loginFormUrl) {
super(loginFormUrl);
}
#Override
public void commence(final HttpServletRequest request, final HttpServletResponse response, final AuthenticationException authException) throws IOException, ServletException {
if(isPreflight(request)){
response.setStatus(HttpServletResponse.SC_NO_CONTENT);
} else if (isRestRequest(request)) {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
} else {
super.commence(request, response, authException);
}
}
/**
* Checks if this is a X-domain pre-flight request.
* #param request
* #return
*/
private boolean isPreflight(HttpServletRequest request) {
return "OPTIONS".equals(request.getMethod());
}
/**
* Checks if it is a rest request
* #param request
* #return
*/
protected boolean isRestRequest(HttpServletRequest request) {
return requestMatcher.matches(request);
}
}
And then add the appropriate xml config
<security:http disable-url-rewriting="true" use-expressions="true"
entry-point-ref="ajaxAwareLoginUrlAuthenticationEntryPoint">
<security:form-login
authentication-failure-url="/login?error=1"
login-processing-url="/login/submit"/>
<security:logout logout-url="/logout" logout-success-url="/login"/>
<security:intercept-url pattern="/" access="permitAll" />
<security:intercept-url pattern="/css/**" access="permitAll" />
<security:intercept-url pattern="/images/**" access="permitAll" />
<security:intercept-url pattern="/js/**" access="permitAll" />
<security:intercept-url pattern="/login" access="permitAll" />
<security:intercept-url pattern="/**" access="isAuthenticated()" />
</security:http>
<bean id="ajaxAwareLoginUrlAuthenticationEntryPoint" class="com.bmchild.service.user.AjaxAwareLoginUrlAuthenticationEntryPoint">
<constructor-arg value="/login" />
</bean>

How to Intercept Spring Security login request before its processing?

I am trying to intercept the login request before spring security start its processing or it reaches to spring security interceptor. I want to do this because I have to do some validations on the user's input. So, I have created a interceptor from HandlerInterceptorAdapter class but it is not working.
So what I want to do is :
When any user try to login with username and password then this request must reaches to LoginRequestInterceptor first. Do some validation on user inputs and on success pass this request to spring security login processing url other wise to some other url.
But in my case the request is reaching directly to the spring security without visiting the interceptor.
Interceptor class
public class LoginRequestInterceptor extends HandlerInterceptorAdapter {
private static final Logger logger = LogManager.getLogger(LoginRequestInterceptor.class);
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
logger.debug("Interceptor working pre");
return super.preHandle(request, response, handler);
}
Spring Security xml
<http auto-config="true" use-expressions="true">
<intercept-url pattern="/Candidate/**" access="hasRole('ROLE_USER')" />
<!-- access denied page -->
<access-denied-handler error-page="/403" />
<form-login
login-processing-url="/login"
login-page="/"
default-target-url="/Candidate"
authentication-failure-url="/error=1"
username-parameter="registerationid"
password-parameter="password"
/>
<logout logout-success-url="/?logout" />
<custom-filter ref="loginRequestInterceptors" position="FIRST"/>
<!-- enable csrf protection -->
<csrf />
</http>
<authentication-manager id="authenticationManager">
<authentication-provider user-service-ref="candidateDetailServices" />
</authentication-manager>
Spring dispatcher xml
<bean id="loginRequestInterceptor" class="org.ibps.clerk.inteceptors.login.LoginRequestInterceptor"></bean>
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/login"/>
<ref bean="loginRequestInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
</beans>
So at last I wanted to know whether it is possible or not? If Yes then please share the link or solution.
You should create a class (for example CustomFilter) extend UsernamePasswordAuthenticationFilter and over ride attemptAuthentication and do your stuff in it and then call super.attemptAuthentication;
Then you should confgiure in your security config class which extends WebSecurityConfigurerAdapter.
#Configuration
#EnableWebSecurity
public class SecurityConfigs extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterBefore(getCustomFilter()
,UsernamePasswordAuthenticationFilter.class)
.formLogin()
.and() // more configs ...
}
public CustomFilter getCustomFilter() throws Exception {
CustomFilter filter= new CustomFilter ("/loginuser","POST");
filter.setAuthenticationManager(authenticationManagerBean());
filter.setAuthenticationFailureHandler((request, response, exception) -> {
response.sendRedirect("/login?error");
});
return filter;
}
And your CustomFilter class:
public class CustomFilter extends UsernamePasswordAuthenticationFilter {
public CustomFilter (String urlLogin, String method) {
setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher(urlLogin, method));
}
#Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
// do your stuff ...
return super.attemptAuthentication(request, response);
}
}
What you're probably looking to do is create a servlet filter that is executed before the Spring Security filterchain. By default the springSecurityFilterChain filter's order value is set to 0, meaning that it is executed before all other filters. One workaround for this is to set the security.filter-order to a higher value than that of the filter you wish to run before its execution in your properties file.
Check Filter order in spring-boot for further discussion on this topic.
Additional resources:
https://github.com/spring-projects/spring-security-oauth/issues/1024
https://mtyurt.net/2015/07/15/spring-how-to-insert-a-filter-before-springsecurityfilterchain/

Convert Spring xml configuration to Java configuration under Spring Boot

I am trying to migrate existing application Spring security configuration written in XML to Java Configuration (with Spring Boot)
Can you please help as to how the Java config needs to be defined for the below xml. I keep getting confused looking at different webpages..
Thanks in advance!
<security:http auto-config="false" entry-point-ref="filterEntryPoint">
<security:custom-filter before="FORM_LOGIN_FILTER" ref="myWebAuthorizationFilter" />
<security:intercept-url pattern="/**" access="ROLE_USER,ROLE_EVP"/>
</security:http>
<bean id="filterEntryPoint"
class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
<property name="loginFormUrl">
<value>https://companyLogin.com</value>
</property>
<property name="forceHttps">
<value>false</value>
</property>
</bean>
For this part, you will need to define a custom filter
<security:http auto-config="false" entry-point-ref="filterEntryPoint">
<security:custom-filter before="FORM_LOGIN_FILTER" ref="myWebAuthorizationFilter" />
<security:intercept-url pattern="/**" access="ROLE_USER,ROLE_EVP"/>
</security:http>
such as
public class MyCustomFilter extends GenericFilterBean {
#Override
public void doFilter (ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
chain.doFilter(request, response);
}
And then register the custom filter
#Configuration
public class CustomWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.addFilterBefore(new MyCustomFilter(), UsernamePasswordAuthenticationFilter.class);
.authorizeRequests()
.antMatchers("/**").access("hasRole('ROLE_USER') and hasRole('ROLE_EVP')");
}
}
To learn more about custom filter click here.
Next, for this one
<bean id="filterEntryPoint"
class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
<property name="loginFormUrl">
<value>https://companyLogin.com</value>
</property>
<property name="forceHttps">
<value>false</value>
</property>
You need to define a bean
#Configuration
public class CustomWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.addFilterBefore(new MyCustomFilter(), UsernamePasswordAuthenticationFilter.class);
.authorizeRequests()
.antMatchers("/**").access("hasRole('ROLE_USER') and hasRole('ROLE_EVP')");
//defined bean
#Bean
public LoginUrlAuthenticationEntryPoint filterEntryPoint() {
LoginUrlAuthenticationEntryPoint entryPoint = new LoginUrlAuthenticationEntryPoint("https://companyLogin.com");
entryPoint.setForceHttps(false);
return entryPoint;
}
}
I think that's all.

Oauth authorization server with custom authentication manager in Java config

I have multiple authentication managers in the application. I distinguish them by bean name. Part of my xml configuration related to oauth authorization server looks like and it works fine:
<oauth:expression-handler id="oauthExpressionHandler" />
<oauth:web-expression-handler id="oauthWebExpressionHandler" />
<oauth:authorization-server client-details-service-ref="clientDetails" token-services-ref="tokenServices" user-approval-handler-ref="userApprovalHandler" >
<oauth:authorization-code disabled="true" />
<oauth:implicit disabled="false" />
<oauth:refresh-token disabled="false" />
<oauth:client-credentials disabled="false" />
<oauth:password authentication-manager-ref="authenticationManager" />
</oauth:authorization-server>
<oauth:resource-server id="resourceServerFilter" resource-id="resource-id" token-services-ref="tokenServices" />
<sec:authentication-manager id="clientAuthenticationManager">
<sec:authentication-provider user-service-ref="clientDetailsUserService" />
</sec:authentication-manager>
<http pattern="/oauth/token" create-session="stateless" authentication-manager-ref="clientAuthenticationManager"
xmlns="http://www.springframework.org/schema/security">
<intercept-url pattern="/oauth/token" access="IS_AUTHENTICATED_FULLY" />
<anonymous enabled="false" />
<http-basic entry-point-ref="clientAuthenticationEntryPoint" />
<!-- include this only if you need to authenticate clients via request parameters -->
<custom-filter ref="oauthClientCredentialsTokenEndpointFilter" before="BASIC_AUTH_FILTER" />
<access-denied-handler ref="oauthAccessDeniedHandler" />
</http>
I'm trying to move it to Java based configuration (in some SecurityConfig class), without lack so far. I've tried something like:
#Configuration
#EnableAuthorizationServer
protected static class OAuth2AuthConfig extends AuthorizationServerConfigurerAdapter {
#Resource(name = "authenticationManager")
private AuthenticationManager authenticationManager;
#Resource
private OAuth2AuthenticationEntryPoint authenticationEntryPoint;
#Resource(name = "clientDetails")
private ClientDetailsService clientDetailsService;
#Resource
private TokenStore tokenStore;
#Resource
private TokenStoreUserApprovalHandler userApprovalHandler;
#Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security.authenticationEntryPoint(authenticationEntryPoint);
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager)
.userApprovalHandler(userApprovalHandler)
.tokenStore(tokenStore);
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.withClientDetails(clientDetailsService);
}
}
#Configuration
#EnableResourceServer
protected static class OAuth2ResourceConfig extends ResourceServerConfigurerAdapter {
#Resource
private DefaultTokenServices tokenServices;
#Resource(name = "authenticationManager")
private AuthenticationManager authenticationManager;
#Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources.resourceId(RESOURCE_ID).tokenServices(tokenServices).authenticationManager(authenticationManager);
}
}
however it still complains about multiple authentication managers, although I explicitly set endpoints.authenticationManager(authenticationManager).
With some debugging I can see it tries to configure it within class WebSecurityConfigurerAdapter and it meets multiple authentication manager within authenticationManager(). Am I able to override it or what am I missing?
AuthorizationServer - here there is a way to prevent Spring to fail on
org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter#authenticationManager by simply overriding method
org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerSecurityConfiguration#configure(org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder) - explanation
ResourceServer - unfortunately there is no way for similar handling corresponding problem. Best what you can do is decreasing number of instances of global authentication managers to exactly one.

HTTP Status 403 if I go to the login page after being already logged in (Spring security login) [duplicate]

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.

Categories