Spring 3 将 UserDetails 添加到会话

Posted

技术标签:

【中文标题】Spring 3 将 UserDetails 添加到会话【英文标题】:Spring 3 adding UserDetails to session 【发布时间】:2012-03-11 18:24:25 【问题描述】:

我正在尝试使用 Spring 3 实现数据库驱动的用户登录设置。 我的日志显示用户使用登录表单中提供的用户名/密码验证成功,但由于不再将用户硬编码到配置中,因此似乎无法在 JSP 中访问该用户。

感觉 UserDetails 对象由于某种原因在会话/安全上下文中不可用。

在我的 security-config.xml 中: 旧的硬编码风格...

    <authentication-provider>
    <user-service>
        <user name="reports" password="reports" authorities="ROLE_REPORTS" />
        <user name="admin" password="admin" authorities="ROLE_REPORTS, ROLE_ADMIN" />
        <user name="super" password="super" authorities="ROLE_REPORTS, ROLE_ADMIN, ROLE_SUPER_ADMIN"/>
    </user-service>
</authentication-provider>

新的身份验证提供者...

<authentication-manager alias="authenticationManager">
    <authentication-provider user-service-ref="userService">
        <!-- 
        <password-encoder ref="pwdEncoder" >
            <salt-source ref="saltSource"/>
        </password-encoder>
         -->
    </authentication-provider>
</authentication-manager>

来自我的用户服务的片段(它实现了 Spring UserDetailsS​​ervice 接口):

    @Transactional
public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException, DataAccessException 
    DbUser dbUser = null;
    List<GrantedAuthority> roles = new ArrayList<GrantedAuthority>();

    Session sess = sessionFactory.getCurrentSession(); 
    Query query = sess.createQuery("from DbUser where userName = '"+userName+"'");

    Iterator<Object> it = query.iterate();
    if(it.hasNext()) 
        if(it.hasNext()) 
            dbUser = (DbUser) it.next();
        
    

    if (dbUser == null) 
        throw new UsernameNotFoundException("user not found");
     else 
        for (GrantedAuthority auth : dbUser.getRoles()) 
            roles.add(new Role(auth.getAuthority()));
        
    

    UserDetails springUser = null;
    springUser =  new org.springframework.security.core.userdetails.User(
        dbUser.getUserName(), 
        dbUser.getPassword().toLowerCase(),
        dbUser.getActive(), 
        true, true, true,
        roles );

    return springUser;

来自我的登录控制器:

    @RequestMapping(value="/login/submit", method=RequestMethod.POST)
public String login(Principal principle, HttpServletRequest request, ModelMap model)
    String username = request.getParameter("username");
    String password = request.getParameter("password");

    UserDetails springUser = null;
    try 
        springUser = userService.loadUserByUsername(username);
     catch(UsernameNotFoundException e) 
        e.printStackTrace();
        model.addAttribute("message","User not found");
        return loginError(model);
    
    if(springUser.getPassword().equals(password))          // user/pwd combo is correct
        /**
         * @todo make sure user ends up as the session user here
         */

// SecurityContext context = SecurityContextHolder.getContext(); // //context.setAuthentication(springUser); // System.out.println("** LoginController 用户验证成功:"+context.getAuthentication().getName()); // System.out.println("** LoginController原理:"+context.getAuthentication().getPrincipal()); 别的 返回登录错误(模型);

    Collection<GrantedAuthority> roles = springUser.getAuthorities();
    for(GrantedAuthority auth : roles)
        if(auth.getAuthority().equals(Role.ROLE_ADMIN))
            return "admin/home";
        
    

    model.addAttribute("message", "Logged in");
    return "reports/summaries";

您会在上面的控制器中看到,我认为我希望能够访问会话中的 UserDetails 对象,但是那里的任何日志记录都只会返回字符串“anonymousUser”。

报告/摘要 jsp 显示正常,但任何数据除外。 JSP 中包含在安全标记中的任何内容都不会显示。 例如,在显然以管理员用户身份成功登录后丢失了这个块:

        <sec:authorize access="hasRole('ROLE_ADMIN')">
        <li>
            <a href="<c:url value="/admin/home"/>">Admin</a>
            <ul>
                <li><a href="<c:url value="/admin/form/checkpoints"/>">Checkpoints</a></li>
                <li><a href="<c:url value="/admin/form/guards"/>">Guards</a></li>
                <li><a href="<c:url value="/admin/form/incidents"/>">Incidents</a></li>
                <li><a href="<c:url value="/admin/form/routes"/>">Routes</a></li>
            </ul>   
        </li>
    </sec:authorize>

正如您现在可能已经收集到的,我是 Spring/Spring MVC 的新手。如果有人可以建议我缺少的 Spring 身份验证框架的一些基本方面,那就太好了!

跟进/编辑: 我已经尝试恢复到 @NimChimpsky 在 cmets 中建议的表单样式。我在身份验证提供程序的设置中有 2 个选项,第一个选项有效:

<authentication-provider>
        <user-service>
            <user name="reports" password="reports" authorities="ROLE_REPORTS" />
            <user name="admin" password="admin" authorities="ROLE_REPORTS, ROLE_ADMIN" />
            <user name="super" password="super" authorities="ROLE_REPORTS, ROLE_ADMIN, ROLE_SUPER_ADMIN"/>
        </user-service>
    </authentication-provider>

第二个,没有:

<beans:bean id="userService" class="uk.co.romar.guardian.services.UserServiceImpl" />

..

当我尝试使用 UserServiceImpl 类进行身份验证时,我在 Hibernate sessionFactory 上收到了 NullPointerException,结果证明 SessionFactory 或我的其他服务都没有像我预期的那样被注入。

应该注入的 sessionFactory bean 在我的 hibernate-context.xml 中定义,它通过我的 servlet.xml 配置中的 import 语句包含。然而,身份验证提供程序是在 security-context.xml 中声明的,它不引用 hibernate-context.xml。所以我将导入语句复制到安全上下文中,希望能够对注入/实例化问题进行排序,但这也无济于事。

【问题讨论】:

【参考方案1】:

我认为你让它变得比它需要的更复杂,弹簧为你做了所有的工作。您不必为提交登录表单编写控制器。像这样使用 spring_secruity_login :

<form name="f" id="f" action="<c:url value='j_spring_security_check'/>" method="POST">
  <label for="username">Username :&nbsp;</label>
  <input type='text' id='j_username' name='j_username'/><br>
  <label for="password">Password :&nbsp;</label>
  <input type='password' id='j_password' name='j_password'><br>

登录后(只需提交上面的表单,您就可以在jsp中访问主体,导入:

<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags"%>

然后显示(或使用 getAuthorities 访问角色):

Username is  <sec:authentication property="principal.username" />

【讨论】:

感谢@NimChimpsky,在添加用户服务之前,我使用的是没有控制器的表单样式。我认为控制器是在阅读各种教程后添加的。我现在遇到了一个不同的问题(SessionFactory 没有被注入到用户服务中,我认为这不相关),当我排序后我会尝试返回这个表单 将 context:component-scan 添加到我的 security-contect 似乎已经解决了其他注入问题。其他地方的问题,但我会针对这些问题提出新问题。

以上是关于Spring 3 将 UserDetails 添加到会话的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Spring Security 3 检索 UserDetails?

Spring security 3.2:自定义 UserDetails 和 UserDetailsS​​ervice 是不是需要自定义 AuthenticationManager?

在 Spring Security 中,UserDetails 将密码存储为字符串,但我将其存储为 byte[]

如何创建数据类实现 Spring Security 特定的 UserDetails

Spring security Userdetails 无法转换为我自己的用户实现

Spring Security 返回来宾而不是 Authentication.getPrincipal() 的 UserDetails