我是Spring的新手,目前正在学习Spring Web Security登录并遇到了一些问题。我使用的是Spring Security 5,JSF 2.2和Primefaces 6.1
首先,我从未调用LoginBean
处的身份验证方法。它将通过方法loadUserByUsername()
直到返回UserDetails
对象,但我的bean中的方法永远不会被调用。我的另一个好奇是UserDetails
对象将在loadUserByUsername()
方法中返回的位置?以下是我的实施:
的 LoginBean.java
@ManagedBean
@SessionScoped
public class LoginBean extends BaseBean
{
@Autowired
private transient AuthenticationManager authenticationManager;
private String username;
private String password;
public String login()
{
try
{
Authentication request = new UsernamePasswordAuthenticationToken(username, password);
Authentication result = authenticationManager.authenticate(request);
SecurityContextHolder.getContext().setAuthentication(result);
if (result.isAuthenticated())
{
Set<String> roles = AuthorityUtils.authorityListToSet(result.getAuthorities());
if (roles.contains("ROLE_ADMIN"))
return SystemUtils.getSavedUrl("admin");
else if (roles.contains("ROLE_USER"))
return SystemUtils.getSavedUrl("user");
}
} catch (BadCredentialsException badCredentialsException)
{
FacesMessage facesMessage = new FacesMessage(
"Login Failed: please check your username/password and try again.");
FacesContext.getCurrentInstance().addMessage(null, facesMessage);
} 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;
}
}
SecurityConfig.java
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter
{
@Autowired
private UserDetailsService userService;
@Override
public void configure(WebSecurity web) throws Exception
{
web.debug(true);
}
@Override
protected void configure(HttpSecurity http) throws Exception
{
http.authorizeRequests().antMatchers("/admin/**").access("hasRole('ADMIN')");
http.authorizeRequests().antMatchers("/user/**").access("hasRole('USER')");
// login
http.formLogin().loginPage("/login.xhtml").permitAll()
.successHandler(getAuthenticationSuccessHandler())
.failureHandler(getAuthenticationFailureHandler());
// logout
http.logout().logoutSuccessUrl("/index.xhtml").invalidateHttpSession(true);
// require all requests to be authenticated except for the resources
http.authorizeRequests().antMatchers("/javax.faces.resource/**", "/**").permitAll().anyRequest()
.authenticated();
// not needed as JSF 2.2 is implicitly protected against CSRF
http.csrf().disable();
http.exceptionHandling().accessDeniedPage("/403.xhtml");
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception
{
auth.authenticationProvider(getAuthenticationProvider());
}
@Bean
public SimpleUrlAuthenticationFailureHandler getAuthenticationFailureHandler()
{
return new CustomeAuthFailureHandler();
}
@Bean
public SimpleUrlAuthenticationSuccessHandler getAuthenticationSuccessHandler()
{
return new CustomAuthSuccessHandler();
}
@Bean
public AuthenticationProvider getAuthenticationProvider() throws Exception
{
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setUserDetailsService(userService);
provider.setPasswordEncoder(new BCryptPasswordEncoder());
return provider;
}
@Bean(name = "authenticationManager")
@Override
public AuthenticationManager authenticationManagerBean() throws Exception
{
return super.authenticationManagerBean();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
AccountServiceImpl.java (实现UserDetailsService
的类)
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException
{
System.out.println("username is:-" + username);
//My account model is implement UserDetails interface
AccountModel user = repository.findByUsername(username);
if (user == null)
{
throw new UsernameNotFoundException("No such user: " + username);
}
if (user.getRole().isEmpty())
{
throw new UsernameNotFoundException("User" + username + "has no authorities");
}
System.out.println("Password is:-" + user.getPassword().toString());
List<GrantedAuthority> authorities = buildUserAuthority(user.getRole());
user.setAuthorities(new HashSet<>(authorities));
return user;
}
private List<GrantedAuthority> buildUserAuthority(Set<RoleModel> userRoles)
{
Set<GrantedAuthority> setAuths = new HashSet<GrantedAuthority>();
// Build user's authorities
for (RoleModel userRole : userRoles)
{
setAuths.add(new SimpleGrantedAuthority(userRole.getRole()));
}
List<GrantedAuthority> result = new ArrayList<GrantedAuthority>(setAuths);
return result;
}
login.xhtml
<h:form prependId="false">
<p:messages showDetail="true" closable="true" autoUpdate="true"
globalOnly="true" />
<p:panelGrid>
<p:row>
<p:column>
<p:outputLabel for="username" value="Username: " />
</p:column>
<p:column>
<p:inputText id="username" value="#{loginBean.username}" />
</p:column>
</p:row>
<p:row>
<p:column>
<p:outputLabel for="password" value="Password: " />
</p:column>
<p:column>
<p:password id="password" value="#{loginBean.password}" />
</p:column>
</p:row>
<p:row>
<p:column colspan="2">
<p:commandButton value="Login" process="@form" ajax="false"
actionListener="#{loginBean.login}" />
</p:column>
</p:row>
</p:panelGrid>
</h:form>
通过上面的实现,虽然根据数据库中的数据正确提供了用户名和密码,但登录永远不会成功。我的实施或配置有什么问题吗?另一个问题是虽然在UsernameNotFoundException
中抛出了loadUserByUsername
,但是异常消息仍未显示在控制台中(但是其他异常消息将显示,只有此异常不会出现),这是正常的吗?
**** 更新 ****
我试图关注this site。
请确保:
- 您没有覆盖login-processing-url的默认映射(默认值为“/ login”,因为选择“/ j_spring_security_check”为
旧版本的Spring- 将用户和密码输入按钮的ID分别设置为“用户名”和“密码”(请注意较旧的Spring版本具有
默认设置为“j_xxx”;你可以实际配置这些值 登录的用户名参数和密码参数属性
元件)- 在按钮定义中使用ajax =“false”
- 将prependId =“false”添加到表单定义
- 如上所示设置FORWARD和REQUEST调度程序参数
如第一点所述,我删除loginPage("/login.xhtml")
,但当您登录指向您的客户登录页面的网址localhost:8080/Project/login.xhtml
时,它只能仅。如果您通过localhost:8080/Project/login
的弹出默认登录页面进行访问,则不起作用
问题是login.xhtml
不是默认登录页面,因此在某些情况下,如果用户正在访问需要凭据的页面,它会将用户重定向到默认登录页面/login
而不是我的登录页面{ {1}}。
我还尝试将其设置为/login.xhtml
,但如果您访问loginPage("/login")
之类的网址,则会返回404。