Spring安全性 - 限制对我的更新配置文件页面的访问

时间:2015-06-18 21:36:23

标签: java spring spring-mvc spring-security

我在我的应用程序中使用spring security并遇到了问题。我的应用程序有一个Update Profile页面。我已将preAuthorized()与请求映射添加为

@PreAuthorize("isAuthenticated()")
@RequestMapping (value="/user/{uid}/profile/update", method = GET)
public String updateProfileView(@ModelAttribute("form") UserProfileForm form, @PathVariable ("uid") Integer userId, Model model){

工作正常,unauthenticated用户无法访问此页面。

但问题在于every Authenticated User can access this page

例如:用户A登录到应用程序后,他/她将能够更新每个人的个人资料。

我的CustomUserDetailService类是

@Service
@Transactional
public class CustomUserDetailsService implements UserDetailsService {

@Resource
UserService userService;

@Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
    com.analyst.future.domain.User user = userService.getUser(email);

    SimpleGrantedAuthority auth = new SimpleGrantedAuthority("ROLE_USER");

    Collection<SimpleGrantedAuthority> authorities = new HashSet<SimpleGrantedAuthority>();
    authorities.add(auth);

    User userDeatails = new User(user.getEmail(), user.getPassword(), authorities);
    return userDeatails;

}

}

我认为我不能用roles来限制它,因为每个经过身份验证的用户都会有相同的角色。

我是否可以限制经过身份验证的用户仅访问自update profile页。

2 个答案:

答案 0 :(得分:1)

我不是Spring安全专家,但尝试阅读使用基于表达式的访问 - Link here

有一条很小的小线符合你想要的 -

  

例如,如果您希望某个特定方法仅允许访问其用户名与联系人的用户名匹配的用户,则可以编写

@PreAuthorize("#contact.name == authentication.name")
public void doSomething(Contact contact);

我认为在你的情况下它会像

@PreAuthorize("email == authentication.email")

这是方法级别,所以也许不是你想要的?好消息是有一种方法可以使用登录用户并将其与请求用户进行匹配。 :)

答案 1 :(得分:0)

由于之前的所有答案都讨论匹配用户名(包含在主体对象中),但您需要匹配用户 ID,因此需要做更多工作。我们首先需要返回一个扩展 UserDetails 的自定义 User 对象。您可以这样做:

第 1 步:使您的“用户”模型实现 UserDetails

@Entity
public class UserAccount implements UserDetails {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @JsonProperty("id")
    private int userId;

    @Column(name = "first_name")
    private String firstName;

    @Column(name = "last_name")
    private String lastName;

    private String email;
    private String password;

    // other fields and methods
}

您还必须覆盖 UserDetails 中的一些方法,这很容易弄清楚。

第 2 步:让您的用户服务实现 UserDetailsService

@Component
public class UserAccountService implements UserDetailsService {

    @Override
    public UserDetails loadUserByUsername(String username) {
        // todo
    }
}

第 3 步:从 loadUserByUsername 方法返回您的用户模型

@Override
public UserDetails loadUserByUsername(String username) {
    Optional<UserAccount>  userAccount = userAccountDao.findByEmail(username);

    if (!userAccount.isPresent()) {
        throw new MyException("User account not found for the given Username.", HttpStatus.NOT_FOUND);
    }

    return userAccount.get();
}

第 4 步:将 @PreAuthorize 表达式添加到您的资源中

@PutMapping(value = "/{userId}")
@PreAuthorize("#userId == authentication.principal.userId or hasAuthority('ADMIN')")
public UserAccount updateUserAccount(@PathVariable("userId") int userId,
                                         @RequestBody UserAccount userAccount) {
    return userAccountService.updateUserAccount(userId, userAccount);
}

上面需要注意的重要事项是:

  • 我们可以检查上面的 userId 是否相等,因为我们的自定义 UserAccount 模型有一个 userId。如果我们返回了一个原始的 UserDetail(如 org.springframework.security.core.userdetails.User)对象,它不会有任何 'id' 属性与之匹配,所以上面会失败。

  • 以上 @PreAuthorize 注释检查 2 个条件。它允许请求如果用户是帐户的所有者如果用户具有管理员权限。< /p>