package sample.context.security;
import java.util.*;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.security.core.*;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.*;
import lombok.Setter;
import sample.context.actor.Actor;
/**
* Spring Securityで利用される認証/認可対象となるユーザ情報を提供します。
*/
@Setter
public class SecurityActorFinder {
@Autowired
private SecurityProperties props;
@Autowired
@Lazy
private SecurityUserService userService;
@Autowired(required = false)
@Lazy
private SecurityAdminService adminService;
/** 現在のプロセス状態に応じたUserDetailServiceを返します。 */
public SecurityActorService detailsService() {
return props.auth().isAdmin() ? adminService() : userService;
}
private SecurityAdminService adminService() {
return Optional.ofNullable(adminService)
.orElseThrow(() -> new IllegalStateException("SecurityAdminServiceをコンテナへBean登録してください。"));
}
/**
* 現在有効な認証情報を返します。
*/
public static Optional<Authentication> authentication() {
return Optional.ofNullable(SecurityContextHolder.getContext().getAuthentication());
}
/**
* 現在有効な利用者認証情報を返します。
* <p>ログイン中の利用者情報を取りたいときはこちらを利用してください。
*/
public static Optional<ActorDetails> actorDetails() {
return authentication().map((auth) -> {
return (auth.getDetails() instanceof ActorDetails) ? (ActorDetails) auth.getDetails() : null;
});
}
/**
* 認証/認可で用いられるユーザ情報。
* <p>プロジェクト固有にカスタマイズしています。
*/
public static class ActorDetails implements UserDetails {
private static final long serialVersionUID = 1L;
/** ログイン中の利用者情報 */
private Actor actor;
/** 認証パスワード(暗号化済) */
private String password;
/** 利用者の所有権限一覧 */
private Collection<GrantedAuthority> authorities;
public ActorDetails(Actor actor, String password, Collection<GrantedAuthority> authorities) {
this.actor = actor;
this.password = password;
this.authorities = authorities;
}
public ActorDetails bindRequestInfo(HttpServletRequest request) {
//low: L/B経由をきちんと考えるならヘッダーもチェックすること
actor.setSource(request.getRemoteAddr());
return this;
}
public Actor actor() {
return actor;
}
@Override
public String getUsername() {
return actor.getId();
}
@Override
public String getPassword() {
return password;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorities;
}
public Collection<String> getAuthorityIds() {
return authorities.stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList());
}
}
/** Actorに適合したUserDetailsService */
public static interface SecurityActorService extends UserDetailsService {
/**
* 与えられたログインIDを元に認証/認可対象のユーザ情報を返します。
* @see org.springframework.security.core.userdetails.UserDetailsService#loadUserByUsername(java.lang.String)
*/
@Override
public abstract ActorDetails loadUserByUsername(String username) throws UsernameNotFoundException;
}
/** 一般利用者向けI/F */
public static interface SecurityUserService extends SecurityActorService {
}
/** 管理者向けI/F */
public static interface SecurityAdminService extends SecurityActorService {
}
}