到达用户ID而不通过每个控制器

时间:2013-10-27 18:11:00

标签: spring spring-mvc spring-security principal

我有一个设计问题,我不知道如何解决。我正在使用Spring 3.2.4和Spring security 3.1.4。

我的数据库中有一个Account表,如下所示:

create table Account (id identity,
                        username varchar unique,
                        password varchar not null,
                        firstName varchar not null, 
                        lastName varchar not null,
                        university varchar not null,
                        primary key (id));

直到最近,我的用户名只是一个用户名,但我将其更改为电子邮件地址,因为许多用户希望使用该用户名登录。

我有一个标题,我在所有页面上都包含了一个链接到用户配置文件的链接:

<a href="/project/users/<%= request.getUserPrincipal().getName()%>" class="navbar-link"><strong><%= request.getUserPrincipal().getName()%></strong></a>

问题是<%= request.getUserPrincipal().getName()%>现在返回电子邮件,我不想将用户链接到他们的电子邮件。相反,我想使用每个用户必须链接到个人资料的ID。

如何从每个页面覆盖用户ID?

我一直在考虑两种解决方案,但我不确定:

  1. 更改主体以包含ID,不知道如何执行此操作并在查找有关该主题的良好信息时遇到问题。
  2. 将模型属性添加到包含整个用户的所有控制器中,但这样做真的很难看,就像这样。
  3. Account account = entityManager.find(Account.class, email);
    model.addAttribute("account", account);

    还有更多方法,我不知道哪一个更喜欢。

    我希望它足够清楚,谢谢你对此有任何帮助。

    ======根据回答编辑=======

    我编辑了Account以实现UserDetails,它现在看起来像这样(稍后会修复自动生成的东西):

    @Entity
    @Table(name="Account")
    public class Account implements UserDetails {
    
        @Id
        private int id;
    
        private String username;
    
        private String password;
    
        private String firstName;
    
        private String lastName;
    
        @ManyToOne
        private University university;
    
        public Account() {
    
        }
    
        public Account(String username, String password, String firstName, String lastName, University university) {
            this.username = username;
            this.password = password;
            this.firstName = firstName;
            this.lastName = lastName;
            this.university = university;
        }
    
        public String getUsername() {
            return username;
        }
    
        public String getPassword() {
            return password;
        }
    
        public String getFirstName() {
            return firstName;
        }
    
        public String getLastName() {
            return lastName;
        }
    
        public void setUsername(String username) {
            this.username = username;
        }
    
        public void setPassword(String password) {
            this.password = password;
        }
    
        public void setFirstName(String firstName) {
            this.firstName = firstName;
        }
    
        public void setLastName(String lastName) {
            this.lastName = lastName;
        }
    
        public University getUniversity() {
            return university;
        }
    
        public void setUniversity(University university) {
            this.university = university;
        }
    
        public int getId() {
            return id;
        }
    
        public void setId(int id) {
            this.id = id;
        }
    
        @Override
        public Collection<? extends GrantedAuthority> getAuthorities() {
            // TODO Auto-generated method stub
            return null;
        }
    
        @Override
        public boolean isAccountNonExpired() {
            // TODO Auto-generated method stub
            return false;
        }
    
        @Override
        public boolean isAccountNonLocked() {
            // TODO Auto-generated method stub
            return false;
        }
    
        @Override
        public boolean isCredentialsNonExpired() {
            // TODO Auto-generated method stub
            return false;
        }
    
        @Override
        public boolean isEnabled() {
            // TODO Auto-generated method stub
            return true;
        }
    }
    

    我还添加了

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

    到我的jsp文件并尝试通过

    来获取id
    <sec:authentication property="principal.id" />
    

    这给了我以下

    org.springframework.beans.NotReadablePropertyException: Invalid property 'principal.id' of bean class [org.springframework.security.authentication.UsernamePasswordAuthenticationToken]: Bean property 'principal.id' is not readable or has an invalid getter method: Does the return type of the getter match the parameter type of the setter?
    

    ======根据回答编辑2 =======

    我的应用程序基于春季社交样本,直到现在我从未改变过任何东西。

    这是我认为相关的文件,请告诉我除了这个之外你还需要看看。

    AccountRepository.java

    public interface AccountRepository {
    
        void createAccount(Account account) throws UsernameAlreadyInUseException;
    
        Account findAccountByUsername(String username);
    
    }
    

    JdbcAccountRepository.java

    @Repository
    public class JdbcAccountRepository implements AccountRepository {
    
        private final JdbcTemplate jdbcTemplate;
    
        private final PasswordEncoder passwordEncoder;
    
        @Inject
        public JdbcAccountRepository(JdbcTemplate jdbcTemplate, PasswordEncoder passwordEncoder) {
            this.jdbcTemplate = jdbcTemplate;
            this.passwordEncoder = passwordEncoder;
        }
    
        @Transactional
        public void createAccount(Account user) throws UsernameAlreadyInUseException {
            try {
                jdbcTemplate.update(
                        "insert into Account (firstName, lastName, username, university, password) values (?, ?, ?, ?, ?)",
                        user.getFirstName(), user.getLastName(), user.getUsername(), user.getUniversity(),
                        passwordEncoder.encode(user.getPassword()));
            } catch (DuplicateKeyException e) {
                throw new UsernameAlreadyInUseException(user.getUsername());
            }
        }
    
        public Account findAccountByUsername(String username) {
            return jdbcTemplate.queryForObject("select username, firstName, lastName, university from Account where username = ?",
                    new RowMapper<Account>() {
                        public Account mapRow(ResultSet rs, int rowNum) throws SQLException {
                            return new Account(rs.getString("username"), null, rs.getString("firstName"), rs.getString("lastName"), new University("test"));
                        }
                    }, username);
        }
    
    }
    

    的security.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans:beans xmlns="http://www.springframework.org/schema/security"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:beans="http://www.springframework.org/schema/beans"
        xsi:schemaLocation="http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd
            http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">
    
        <http pattern="/resources/**" security="none" />
        <http pattern="/project/" security="none" />
    
        <http use-expressions="true">
            <!-- Authentication policy -->
            <form-login login-page="/signin" login-processing-url="/signin/authenticate" authentication-failure-url="/signin?error=bad_credentials" />
            <logout logout-url="/signout" delete-cookies="JSESSIONID" />
            <intercept-url pattern="/addcourse" access="isAuthenticated()" />
            <intercept-url pattern="/courses/**/**/edit" access="isAuthenticated()" />
            <intercept-url pattern="/users/**/edit" access="isAuthenticated()" />
        </http>
    
        <authentication-manager alias="authenticationManager">
            <authentication-provider>
                <password-encoder ref="passwordEncoder" />
                <jdbc-user-service data-source-ref="dataSource" 
                                users-by-username-query="select username, password, true from Account where username = ?"
                                authorities-by-username-query="select username, 'ROLE_USER' from Account where username = ?"/>
            </authentication-provider>
            <authentication-provider>
                <user-service>
                    <user name="admin" password="admin" authorities="ROLE_USER, ROLE_ADMIN" />
                </user-service>
            </authentication-provider>
        </authentication-manager>
    
    </beans:beans>
    

    这是我尝试实现UserDetailsS​​ervice

    public class RepositoryUserDetailsService implements UserDetailsService {
    
        private final AccountRepository accountRepository;
    
        @Autowired
        public RepositoryUserDetailsService(AccountRepository repository) {
            this.accountRepository = repository;
        }
    
        @Override
        public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
            Account user = accountRepository.findAccountByUsername(username);
    
            if (user == null) {
                throw new UsernameNotFoundException("No user found with username: " + username);
            }
    
            return user;
    
        }
    }
    

    仍然给我同样的错误,我需要在某个地方添加UserDetailsS​​ervice吗? 与我最初的问题相比,这已经开始成为别的东西,我应该开始另一个问题。

    很抱歉我缺乏这方面的经验。我必须阅读。

2 个答案:

答案 0 :(得分:6)

如果您使用的是Spring Security,那么放弃Scriptlet并使用Spring Security自定义标记库可能是有益的:

http://docs.spring.io/spring-security/site/docs/3.1.4.RELEASE/reference/taglibs.html

只要您的id模型上有getId()Account方法属性,并且您的Account模型正在实施Spring Security&#39; s {{ 1}} interface(应该如此),那么您应该能够使用以下标记访问UserDetails属性:

id

您可以将其内联使用或将值分配给另一个变量:

<security:authentication property="principal.id" />

这适用于<security:authentication property="principal.id" var="accountId" /> ... ${accountId} 对象上的任何属性(只要用户经过身份验证,很明显。)

更新问题后编辑:

确保Account的实施正在返回UserDetailsService的实例。听起来你实际上并没有使用Account类型作为你的校长。

Account

在问题更新后编辑#2

好问题就在这里:

@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {        
    Account account = ... // fetch your Account
    return account;
}

您正在使用&#34;快捷方式&#34;可以这么说来获取Spring Security提供的<jdbc-user-service data-source-ref="dataSource" users-by-username-query="select username, password, true from Account where username = ?" authorities-by-username-query="select username, 'ROLE_USER' from Account where username = ?"/> 接口的默认实现,特别是UserDetails。但是,您希望您的主体属于org.springframework.security.core.userdetails.User类型。这不会自动发生在你身上。您应该提供自己的Account实现,而不是使用jdbc-user-service元素。像这样:

UserDetailsService

...使用<authentication-manager ...> <authentication-provider user-service-ref="myUserDetailsServiceImpl"/> </authentication-manager> 实现,例如:

UserDetailsService

这应该让你朝着正确的方向前进。

答案 1 :(得分:0)

我对春天一无所知,但取决于他们如何扩展身份主体,可能还有其他字段,例如您可以抓取的用户ID。您也可以在spring中配置安全性,并将自己的项目添加到标识集合中。如果这不可行,那么考虑一个actionfilter,它根据电子邮件地址(在某个存储库中)查找用户ID并填充您的模型/视图包。装饰需要它的每个控制器或动作。