我正在尝试使用注释将spring security 4.0.2与primefaces 5.0集成在一起。 这是 web.xml
<context-param>
<description>Configure ContextLoaderListener to use AnnotationConfigWebApplicationContext instead of the default XmlWebApplicationContext</description>
<param-name>contextClass</param-name>
<param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
</context-param>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>com.giong.config.ApplicationConfig</param-value>
</context-param>
<context-param>
<param-name>javax.faces.DEFAULT_SUFFIX</param-name>
<param-value>.xhtml</param-value>
</context-param>
<context-param>
<description>Spring Security Facelets Tag Library</description>
<param-name>javax.faces.FACELETS_LIBRARIES</param-name>
<param-value>classpath*:spring/spring-security.taglib.xml</param-value>
</context-param>
<context-param>
<description>Project Stage Level</description>
<param-name>javax.faces.PROJECT_STAGE</param-name>
<!-- Change to "Production" when you are ready to deploy -->
<param-value>Development</param-value>
</context-param>
<context-param>
<description>State saving method: 'client' or 'server' (=default). See JSF Specification 2.5.2</description>
<param-name>javax.faces.STATE_SAVING_METHOD</param-name>
<param-value>client</param-value>
</context-param>
<context-param>
<param-name>javax.servlet.jsp.jstl.fmt.localizationContext</param-name>
<param-value>resources.application</param-value>
</context-param>
<context-param>
<param-name>pimefaces.THEME</param-name>
<param-value>dot-luv</param-value>
</context-param>
<!-- Spring Security -->
<filter>
<description>Enable Spring Filter: Spring Security works on the concept of Filters</description>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<!-- Defines urls pattern on which the filter is applied -->
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>FORWARD</dispatcher>
<dispatcher>REQUEST</dispatcher>
<!-- mandatory to allow the managed bean to forward the request to the filter -->
</filter-mapping>
<!-- Spring -->
<listener>
<description>The Bootstrap listener to start up and shut down Spring's root WebApplicationContext. It is registered to Servlet Container</description>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener>
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>
<listener>
<listener-class>com.sun.faces.config.ConfigureListener</listener-class>
</listener>
<servlet>
<description>JSF Servlet is defined to container</description>
<display-name>Faces Servlet</display-name>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>*.xhtml</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>pages/index.xhtml</welcome-file>
</welcome-file-list>
弹簧安全配置文件
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final int TOKEN_VALIDITY_SECONDS = 60 * 60 * 24;
@Autowired
private DataSource dataSource;
@Autowired
private UserDetailsService userDetailsService;
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(this.userDetailsService).passwordEncoder(this.passwordEncoder());
}
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/javax.faces.resource/**", "/resources/**");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
// @formatter:off
http
.csrf().disable().httpBasic()
.and()
.headers().cacheControl().and()
.and()
.sessionManagement()
.sessionFixation().none()
.and()
.authorizeRequests().anyRequest().authenticated()
.and()
.formLogin().loginPage("/pages/login.xhtml").permitAll()
.and()
.logout().invalidateHttpSession(true)
.deleteCookies("JSESSIONID", AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY)
.logoutSuccessUrl("/")
.and()
.rememberMe().tokenRepository(this.tokenRepository()).tokenValiditySeconds(this.TOKEN_VALIDITY_SECONDS);
;
// @formatter:on
}
@Override
@Bean(name = "authenticationManager")
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Bean(name = "passwordEncoder")
public PasswordEncoder passwordEncoder() {
final BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
return passwordEncoder;
}
@Bean(name = "tokenRepository")
public PersistentTokenRepository tokenRepository() {
final JdbcTokenRepositoryImpl db = new JdbcTokenRepositoryImpl();
db.setDataSource(this.dataSource);
return db;
}
}
登录页面
<html
xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:p="http://primefaces.org/ui">
<h:head>
<title>#{i18n['login']}</title>
<meta
http-equiv="Content-Type"
content="text/html; charset=UTF-8" />
<h:outputStylesheet
library="css"
name="style.css" />
</h:head>
<h:body>
<div align="center">
<h1>#{i18n['info.welcome_to_leave_application']}</h1>
<p:panel
header="#{i18n['login']}"
style="width: 480px">
<p:growl
showDetail="true"
life="8000" />
<h:form
id="frmLogin"
prependId="false">
<div class="ui-grid ui-grid-responsive">
<div class="ui-grid-row value">
<div class="ui-grid-col-3 label">
<p:outputLabel
for="username"
value="#{i18n['username']}" />
</div>
<div class="ui-grid-col-6 value">
<p:inputText
id="username"
required="true"
requiredMessage="#{i18n['error.please_enter_username']}"
label="#{i18n['username']}" />
</div>
</div>
<div class="ui-grid-row value">
<div class="ui-grid-col-3 label">
<p:outputLabel
for="password"
value="#{i18n['password']} " />
</div>
<div class="ui-grid-col-6 value">
<p:password
id="password"
required="true"
requiredMessage="#{i18n['error.please_enter_password']}"
label="#{i18n['password']}" />
</div>
</div>
<div class="ui-grid-row value">
<div
class="ui-grid-col-4"
align="right">
<h:selectBooleanCheckbox
id="remember-me"
label="#{i18n['remember_me']}"
style="width: 20px;height: 20px;" />
</div>
<div
class="ui-grid-col-5"
align="left">
<p:outputLabel
for="remember-me"
value=" #{i18n['remember_me']}" />
</div>
</div>
</div>
<p:commandButton
id="btnLogin"
value="#{i18n['login']}"
action="#{loginManagedBean.doLogin()}"
icon="ui-icon-person"
ajax="false" />
</h:form>
</p:panel>
</div>
</h:body>
</html>
登录托管bean
@ManagedBean(name = "loginManagedBean")
@RequestScoped
public class LoginManagedBean extends AbtractManagedBean implements PhaseListener {
private static final long serialVersionUID = 1L;
/*
*************************************** ACTIONS ***************************************
*/
@Override
public void afterPhase(PhaseEvent event) {
}
@Override
public void beforePhase(PhaseEvent event) {
final Exception e = (Exception) FacesContext.getCurrentInstance().getExternalContext().getSessionMap().get(WebAttributes.AUTHENTICATION_EXCEPTION);
if (e instanceof BadCredentialsException) {
e.printStackTrace();
FacesContext.getCurrentInstance().getExternalContext().getSessionMap().put(WebAttributes.AUTHENTICATION_EXCEPTION, null);
JSFMessageUtil.sendFatalMessageToUser(JSFMessageUtil.getResource("error.user_pass_is_invalid"), "");
}
else if (e instanceof LockedException || e instanceof DisabledException) {
e.printStackTrace();
FacesContext.getCurrentInstance().getExternalContext().getSessionMap().put(WebAttributes.AUTHENTICATION_EXCEPTION, null);
JSFMessageUtil.sendErrorMessageToUser(JSFMessageUtil.getResource("error.user_acc_has_been_suspended"), "");
}
}
@Override
public PhaseId getPhaseId() {
return PhaseId.RENDER_RESPONSE;
}
public void doLogin() throws ServletException, IOException {
final ExternalContext context = FacesContext.getCurrentInstance().getExternalContext();
final RequestDispatcher dispatcher = ((ServletRequest) context.getRequest()).getRequestDispatcher("/login");
dispatcher.forward((ServletRequest) context.getRequest(), (ServletResponse) context.getResponse());
FacesContext.getCurrentInstance().responseComplete();
}
/*
*************************************** GETTER & SETTER ***************************************
*/
}
如果正确输入用户名/密码,我可以无任何错误登录。 但是有一些问题:
当我忘记输入用户名/密码或输入无效值时,没有任何消息被提供。甚至控制台都没有显示任何内容。
我在LoginManagedBean.doLogin()中切换了断点,但它并没有在断点处停止。我甚至可以删除doLogin()方法的内容,但仍然以普通方式登录。我假设没有调用doLogin()方法,它对我当前的工具不是必需的。但如果我从
<p:commandButton id="btnLogin" value="#{i18n['login']}" action="#{loginManagedBean.doLogin()}" icon="ui-icon-person" ajax="false" />
到
<p:button value="#{loginManagedBean.doLogin()}">#{i18n['login']}</p:button>
调用了doLogin()方法,但是有一个错误: HTTP Status 404 - /leave-application/login
所以,在这种情况下我应该使用p:button还是p:commandButton;以及如何向用户显示身份验证例外?