具有 Spring 安全性的 JSF - 登录后重定向到指定页面
Posted
技术标签:
【中文标题】具有 Spring 安全性的 JSF - 登录后重定向到指定页面【英文标题】:JSF with Spring security - Redirect to specified page after login 【发布时间】:2013-01-05 13:54:02 【问题描述】:我有一个使用 JSF 的 Web 应用程序,并且 Spring 安全性用于登录和授权。
当用户在特定页面中键入但未登录时,例如localhost:8080/APP/page.xhtml
,应用程序重定向到登录页面,这没问题。之后,用户登录,但他被重定向到的页面是index.xhtml
,这是默认的欢迎页面,而不是page.xhtml
。
我想要以下行为:用户转到localhost:8080/APP/page.xhtml
,他被重定向到登录,之后他应该被重定向到他想要的页面 - page.xhtml
。
已编辑 - spring-security.xml 的片段:
<security:http auto-config="true">
<security:intercept-url pattern="/javax.faces.resource/*" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
<security:intercept-url pattern="/login.xhtml" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
<security:intercept-url pattern="/**" access="ROLE_UNALLOCATED, ROLE_MANAGER"/>
<security:intercept-url pattern="/pages/management/" access="ROLE_MANAGER"/>
<security:intercept-url pattern="/pages/requests/" access="ROLE_UNALLOCATED, ROLE_EMPLOYEE, ROLE_TEAM_LEADER, ROLE_MANAGER"/>
<security:form-login login-page="/login.xhtml"
/>
<security:logout logout-url="/j_spring_security_logout"
logout-success-url="/login.xhtml"
invalidate-session="true"/>
</security:http>
有什么想法吗?谢谢
编辑:
我以为是因为:
<!-- Welcome page -->
<welcome-file-list>
<welcome-file>/index.xhtml</welcome-file>
</welcome-file-list>
来自 web.xml。我删除了它,但结果相同。
后期编辑:
我刚刚注意到我的自定义 loginController 中 if (authenticationResponseToken.isAuthenticated())
之后的一行,这几乎肯定是问题的根源:
登录控制器:
@ManagedBean(name = "loginController")
@SessionScoped
@Controller
public class LoginController implements Serializable
private static final long serialVersionUID = 1L;
@Autowired
IUserService userService;
@Autowired
@Qualifier("authenticationManager")
protected AuthenticationManager authenticationManager;
// save the current user after login to be able to inject it in other places
// as needed
private User currentUser;
/**
* This action logs the user in and returns to the secure area.
*
* @return String path to secure area
*/
public String loginUsingSpringAuthenticationManager()
// get backing bean for simple redirect form
LoginFormBackingBean loginFormBean = (LoginFormBackingBean) FacesUtils
.getBackingBean("loginFormBean");
// simple token holder
Authentication authenticationRequestToken = createAuthenticationToken(loginFormBean);
// authentication action
try
Authentication authenticationResponseToken = authenticationManager
.authenticate(authenticationRequestToken);
Authentication authCopy = null;
final Object principal = authenticationResponseToken.getPrincipal();
if (principal instanceof LdapUserDetailsImpl)
LdapUserDetailsImpl userImpl = (LdapUserDetailsImpl) principal;
userImpl.getUsername();
// here check if we already have a User with his DN in the DB
// get the User by DN
User u = userService.getUserByDn(userImpl.getDn());
// if a user with this DN does not exist in the DB, create a new
// one with the DN from LDAP and the default settings for a new
// user
if (null == u)
u = userService.createNewUserFromDn(userImpl.getDn(),
userImpl.getUsername());
// set the obtained user as the current user
setCurrentUser(u);
List<GrantedAuthority> grAuth = new ArrayList<GrantedAuthority>();
// after this, do the role authority stuff
// here loop through user roles if he has more and
if (null != u.getUserTeamRoles())
for (UserTeamRole urt : u.getUserTeamRoles())
// here get role for every UserTeamRole
grAuth.add(new SimpleGrantedAuthority(urt.getRole()
.getName()));
// add the above found roles to the granted authorities of the
// current authentication
authCopy = new UsernamePasswordAuthenticationToken(
authenticationResponseToken.getPrincipal(),
authenticationResponseToken.getCredentials(), grAuth);
SecurityContextHolder.getContext().setAuthentication(authCopy);
// ok, test if authenticated, if yes reroute
if (authenticationResponseToken.isAuthenticated())
// lookup authentication success url, or find redirect parameter
// from login bean
return "index.xhtml?faces-redirect=true";
catch (BadCredentialsException badCredentialsException)
// FacesMessage facesMessage = new FacesMessage(
// "Login Failed: please check your username/password and try again.");
// FacesContext.getCurrentInstance().addMessage(null, facesMessage);
FacesContext.getCurrentInstance().addMessage(
null,
new FacesMessage(FacesMessage.SEVERITY_ERROR,
"Sample error message",
"Login Failed! Please check your credentials"));
catch (LockedException lockedException)
FacesMessage facesMessage = new FacesMessage(
"Account Locked: please contact your administrator.");
FacesContext.getCurrentInstance().addMessage(null, facesMessage);
catch (DisabledException disabledException)
FacesMessage facesMessage = new FacesMessage(
"Account Disabled: please contact your administrator.");
FacesContext.getCurrentInstance().addMessage(null, facesMessage);
return null;
private Authentication createAuthenticationToken(
LoginFormBackingBean loginFormBean)
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
loginFormBean.getUserName(), loginFormBean.getPassword());
return usernamePasswordAuthenticationToken;
如何重定向到我需要的页面?我有一个包含用户名和密码的 loginBean,我可以添加一个 redirectUrl 但如何找到以前的 url 路径?
【问题讨论】:
您能否使用intercept-url
-s 发布完整的http
配置。
我添加了整个 security-http 配置
尝试调试org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler
,看看它是否正在执行,是否重定向到正确的url。
【参考方案1】:
AbstractAuthenticationProcessingFilter 中的 Spring Security implements this logic for you(通常使用 UsernamePasswordAuthenticationFilter 的具体实现)by using the SavedRequestAwareAuthenticationSuccessHandler(在幕后使用 HttpSessionRequestCache)。由于您已经编写了一个控制器来进行身份验证(而不是使用内置支持),因此您需要自己实现此逻辑。您可以重用 Spring Security 所做的相同类,而不是自己实现所有逻辑。
例如,您的 LoginController 可能会更新如下:
public class LoginController implements Serializable
// ... same as before ...
private RequestCache requestCache = new HttpSessionRequestCache();
public String loginUsingSpringAuthenticationManager()
// ... just as you had been doing ...
if (authenticationResponseToken.isAuthenticated())
HttpServletRequest request = (HttpServletRequest)
FacesContext.getCurrentInstance().getExternalContext().getRequest();
HttpServletResponse response = (HttpServletResponse)
FacesContext.getCurrentInstance().getExternalContext().getResponse();
SavedRequest savedRequest = requestCache.getRequest(request, response);
return savedRequest.getRedirectUrl();
// ... same as you had ...
【讨论】:
我这样做了,问题是我的页面使用了 PrimeFaces 组件。可能正因为如此,savedRequest.getRedirectUrl
总是返回:http://localhost:8080/APP/javax.faces.resource/jquery/jquery.js.xhtml?ln=primefaces
。我检查了页面的源代码,并在头组件中找到了相同的声明:<script type="text/javascript" src="/APP/javax.faces.resource/jquery/jquery.js.xhtml?ln=primefaces">
您是否有任何理由需要将其设为私有?除非您有理由保护 javascript(可能只是 jquery 库的副本),否则您似乎应该公开此 URL。也许您打算将所有资源配置为公共资源(即 <security:http pattern="/javax.faces.resource/*" security="none"/>
还是一样。
我不确定它是否只是 SO 格式,但模式 /javax.faces.resource/*
与 /javax.faces.resource/jquery/jquery.js.xhtml?ln=primefaces
不匹配。确保您的模式最后有两个 *
。例如:<security:http pattern="/javax.faces.resource/**" security="none"/>
完全正确,它适用于/javax.faces.resource/**
。非常感谢您的帮助!【参考方案2】:
默认情况下,如果default-target-url
没有定义,Spring Security会尝试重定向到之前的url,所以你只需要从form-login
标签中删除default-target-url
属性。
<security:form-login login-page="/login.xhtml" />
更新
如果您的过滤器链中包含ExceptionTranslationFilter
,那么它应该缓存 HTTP 请求。因此,在您的自定义 loginController 中,您可以尝试从此缓存请求中获取重定向 url。
RequestCache requestCache = new HttpSessionRequestCache();
SavedRequest savedRequest = requestCache.getRequest(request, response);
String targetUrl = savedRequest.getRedirectUrl();
将请求注入您的控制器尝试:
private @Autowired HttpServletRequest request;
或
HttpServletRequest curRequest = ((ServletRequestAttributes)
RequestContextHolder.currentRequestAttributes()) .getRequest();
【讨论】:
这样做了,结果相同。登录后进入 index.xhtml 您使用的是哪个版本的 spring-security?你还在使用默认过滤器堆栈吗? 我使用的是 Spring Security 3.1.1。我不确定默认过滤器堆栈是什么,我正在使用一些改变
<security:form-login login-page="/login.xhtml"
default-target-url="/index.xhtml" />
到
<security:form-login login-page="/login.xhtml"
default-target-url="/page.xhtml" />
【讨论】:
这只会让应用程序一直转到 page.xhtml 而不是 index.xhtml。 我知道。抱歉@AndaP,我看错了你的问题,现在才注意到你在示例地址前面写了eg
【参考方案4】:
要获得您描述的行为,您应该在 web.xml 中添加一个 spring 安全过滤器,如下所示:
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>FORWARD</dispatcher>
<dispatcher>REQUEST</dispatcher>
<dispatcher>ERROR</dispatcher>
<dispatcher>INCLUDE</dispatcher>
</filter-mapping>
您可能还需要编辑您的 spring-secruity.xml。 有关更多信息,请参阅此链接:Spring Security with DelegatingFilterProxy
检查此讨论,它看起来与您的问题相似,可能对您有所帮助:http://forum.springsource.org/showthread.php?128480-Redirecting-to-original-(unauthenticated-page)-after-spring-security-openid-authentic
【讨论】:
我实际上在我的 web.xml 中有该配置(没有 ERROR 和 INCLUDE 情况),但它仍然是相同的行为。我不确定我应该从你给我的链接中修改什么,看起来我有相同的配置。 您在 Spring 安全配置中使用自定义身份验证过滤器吗?我编辑了我的帖子并添加了另一个可能对您有帮助的链接。 能否请您编辑帖子并将您的 loginController 声明和包含 if (authenticationResponseToken.isAuthenticated()) 的方法的实现。以上是关于具有 Spring 安全性的 JSF - 登录后重定向到指定页面的主要内容,如果未能解决你的问题,请参考以下文章
使用Spring Social,Spring安全登录后重定向到原始URL?
Spring Oauth和安全性在登录成功后重定向到/ login
Spring 安全性和 JSF:在登录时调用支持 bean 的方法?