Spring 3 将 UserDetails 添加到会话中

发布于 2025-01-08 04:43:54 字数 5652 浏览 1 评论 0原文

我正在尝试使用 Spring 3 实现数据库驱动的用户登录设置。 我的日志显示用户已使用登录表单中提供的用户名/apssword 进行了身份验证,但自从不再将用户硬编码到配置中后,该用户似乎无法在 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 在评论中建议的表单样式。我在身份验证提供程序设置中有两个选项,第一个有效:

<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。因此,我将导入语句复制到安全上下文中,希望能够解决注入/实例化问题,但这也没有帮助。

I'm trying to implement a DB driven user login setup with Spring 3.
My logs show me the user was authenticated ok using the username/apssword provided in the login form, but the user doesn't seem to be accessible in JSPs since moving away from having the users hardcoded into config.

It feels like the UserDetails object isn't available through the session/securitycontext for some reason.

In my security-config.xml:
Old hardcoded style...

    <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>

New auth-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>

Snippet from my userservice (which implements the Spring UserDetailsService interface):

    @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;
}

From my loginController:

    @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 user validated ok: "+context.getAuthentication().getName());
// System.out.println("** LoginController principle: "+context.getAuthentication().getPrincipal());
} else {
return loginError(model);
}

    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";
}

You'll see in my controller above where I think I would expect to be able to access a UserDetails object in the session, however any logging there only returns the string "anonymousUser".

The reports/summaries jsp is displayed ok, except for any data. Anything in the JSP that is wrapped in security tags doesn't get displayed.
eg missing this chunk after apparently logging in successfully as an admin user:

        <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>

As you've probably gathered by now, I'm new to Spring/Spring MVC. If anyone can suggest some fundamental aspect of Spring authentication framework I'm missing that would be great!

Followup/EDIT:
I've tried reverting back to the form style suggested by @NimChimpsky in comments. I have 2 options in settings for authentication-provider, the first, which works :

<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>

The second, which doesn't:

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

..

When I attempt to authenticate using that UserServiceImpl class I was getting a NullPointerException on the Hibernate sessionFactory, it turns out neither the SessionFactory or my other service are getting injected as I expected.

The sessionFactory bean that should get injected is defined in my hibernate-context.xml, which is included via an import statement in my servlet.xml config. The authentication-provider however is declared in security-context.xml which does not reference hibernate-context.xml. So I copied the import statement into security-context in the hope that would sort the injection/instantiation issue, but that doesn't help either.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

雄赳赳气昂昂 2025-01-15 04:43:54

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

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

登录后(只需提交上述表单,就可以在jsp中访问principal, import :

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

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

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

I think you are making it more complicated than it needs to be, spring does all the leg work for you. You don't have to write a controller for submitting the login form. Use the spring_secruity_login like so :

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

And after logging in (by just submitting the above form, you can then access principal in jsp, import :

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

Then display (or use getAuthorities to access roles) :

Username is  <sec:authentication property="principal.username" />
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文