Redditアプリへのロールと特権の追加
1. 概要
今回の記事では、our Reddit appに簡単な役割と権限を導入して、次のような興味深いことを実行できるようにします。通常のユーザーがRedditに毎日スケジュールできる投稿の数を制限します。
また、管理者の役割(および暗黙的に管理者ユーザー)を持つため、管理者管理領域も追加します。
2. User、Role、およびPrivilegeエンティティ
まず、Userエンティティを変更します(Reddit App seriesを介して使用します)。ロールを追加します。
@Entity
public class User {
...
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(name = "users_roles",
joinColumns = @JoinColumn(name = "user_id", referencedColumnName = "id"),
inverseJoinColumns = @JoinColumn(name = "role_id", referencedColumnName = "id"))
private Collection roles;
...
}
ユーザーとロールの関係が柔軟な多対多であることに注意してください。
次に、RoleエンティティとPrivilegeエンティティを定義します。 その実装の詳細については、this article on exampleを確認してください。
3. セットアップ
次に、プロジェクトのブートストラップでいくつかの基本的なセットアップを実行して、次の役割と特権を作成します。
private void createRoles() {
Privilege adminReadPrivilege = createPrivilegeIfNotFound("ADMIN_READ_PRIVILEGE");
Privilege adminWritePrivilege = createPrivilegeIfNotFound("ADMIN_WRITE_PRIVILEGE");
Privilege postLimitedPrivilege = createPrivilegeIfNotFound("POST_LIMITED_PRIVILEGE");
Privilege postUnlimitedPrivilege = createPrivilegeIfNotFound("POST_UNLIMITED_PRIVILEGE");
createRoleIfNotFound("ROLE_ADMIN", Arrays.asList(adminReadPrivilege, adminWritePrivilege));
createRoleIfNotFound("ROLE_SUPER_USER", Arrays.asList(postUnlimitedPrivilege));
createRoleIfNotFound("ROLE_USER", Arrays.asList(postLimitedPrivilege));
}
そして、テストユーザーを管理者にします。
private void createTestUser() {
Role adminRole = roleRepository.findByName("ROLE_ADMIN");
Role superUserRole = roleRepository.findByName("ROLE_SUPER_USER");
...
userJohn.setRoles(Arrays.asList(adminRole, superUserRole));
}
4. 標準ユーザーの登録
また、registerNewUser()実装を介して標準ユーザーを登録していることを確認する必要があります。
@Override
public void registerNewUser(String username, String email, String password) {
...
Role role = roleRepository.findByName("ROLE_USER");
user.setRoles(Arrays.asList(role));
}
システム内の役割は次のとおりです。
-
ROLE_USER:通常のユーザーの場合(デフォルトの役割) - これらには1日にスケジュールできる投稿の数に制限があります。
-
ROLE_SUPER_USER:スケジューリング制限なし
-
ROLE_ADMIN:追加の管理オプション
5. 校長
次に、これらの新しい特権を主要な実装に統合しましょう。
public class UserPrincipal implements UserDetails {
...
@Override
public Collection extends GrantedAuthority> getAuthorities() {
List authorities = new ArrayList();
for (Role role : user.getRoles()) {
for (Privilege privilege : role.getPrivileges()) {
authorities.add(new SimpleGrantedAuthority(privilege.getName()));
}
}
return authorities;
}
}
6. 標準ユーザーによるスケジュールされた投稿の制限
Redditのスパムを回避するために、新しい役割と特権、およびrestrict standard users from scheduling more than – say – 3 new articles a dayを利用してみましょう。
6.1. 投稿リポジトリ
まず、PostRepositoryの実装に新しい操作を追加します。これにより、特定の期間に特定のユーザーがスケジュールした投稿をカウントします。
public interface PostRepository extends JpaRepository {
...
Long countByUserAndSubmissionDateBetween(User user, Date start, Date end);
}
5.2. スケジュールされたポストコントローラー
次に、schedule()メソッドとupdatePost()メソッドの両方に簡単なチェックを追加します。
public class ScheduledPostRestController {
private static final int LIMIT_SCHEDULED_POSTS_PER_DAY = 3;
public Post schedule(HttpServletRequest request,...) throws ParseException {
...
if (!checkIfCanSchedule(submissionDate, request)) {
throw new InvalidDateException("Scheduling Date exceeds daily limit");
}
...
}
private boolean checkIfCanSchedule(Date date, HttpServletRequest request) {
if (request.isUserInRole("POST_UNLIMITED_PRIVILEGE")) {
return true;
}
Date start = DateUtils.truncate(date, Calendar.DATE);
Date end = DateUtils.addDays(start, 1);
long count = postReopsitory.
countByUserAndSubmissionDateBetween(getCurrentUser(), start, end);
return count < LIMIT_SCHEDULED_POSTS_PER_DAY;
}
}
ここでいくつか興味深いことがあります。 まず、Spring Securityと手動でやり取りし、現在ログインしているユーザーに権限があるかどうかを確認していることに注目してください。 これは毎日行うことではありませんが、行う必要がある場合は、APIが非常に役立ちます。
現在のロジックでは、ユーザーがPOST_UNLIMITED_PRIVILEGEを持っている場合、ユーザーは選択した量だけスケジュールを立てることができます。
ただし、その権限がない場合は、1日あたり最大3件の投稿をキューに入れることができます。
7. 管理者ユーザーページ
次に、ユーザーの役割に基づいてユーザーを明確に分離したので、小さなRedditアプリの管理者向けに非常に簡単なユーザー管理を実装しましょう。
7.1. すべてのユーザーを表示
まず、システム内のすべてのユーザーを一覧表示する基本的なページを作成しましょう。
ここにすべてのユーザーをリストするためのAPI:
@PreAuthorize("hasRole('ADMIN_READ_PRIVILEGE')")
@RequestMapping(value="/admin/users", method = RequestMethod.GET)
@ResponseBody
public List getUsersList() {
return service.getUsersList();
}
そして、サービス層の実装:
@Transactional
public List getUsersList() {
return userRepository.findAll();
}
次に、シンプルなフロントエンド:
Username
Roles
Actions
7.2. ユーザーの役割を変更する
次に、これらのユーザーの役割を管理するためのいくつかの単純なロジック。コントローラから始めましょう:
@PreAuthorize("hasRole('USER_WRITE_PRIVILEGE')")
@RequestMapping(value = "/user/{id}", method = RequestMethod.PUT)
@ResponseStatus(HttpStatus.OK)
public void modifyUserRoles(
@PathVariable("id") Long id,
@RequestParam(value = "roleIds") String roleIds) {
service.modifyUserRoles(id, roleIds);
}
@PreAuthorize("hasRole('USER_READ_PRIVILEGE')")
@RequestMapping(value = "/admin/roles", method = RequestMethod.GET)
@ResponseBody
public List getRolesList() {
return service.getRolesList();
}
そしてサービス層:
@Transactional
public List getRolesList() {
return roleRepository.findAll();
}
@Transactional
public void modifyUserRoles(Long userId, String ids) {
List roleIds = new ArrayList();
String[] arr = ids.split(",");
for (String str : arr) {
roleIds.add(Long.parseLong(str));
}
List roles = roleRepository.findAll(roleIds);
User user = userRepository.findOne(userId);
user.setRoles(roles);
userRepository.save(user);
}
最後に-シンプルなフロントエンド:
Modify User Roles
8. セキュリティ構成
最後に、セキュリティ構成を変更して、管理ユーザーをシステム内のこの新しい個別のページにリダイレクトする必要があります。
@Autowired
private AuthenticationSuccessHandler successHandler;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.
...
.authorizeRequests()
.antMatchers("/adminHome","/users").hasAuthority("ADMIN_READ_PRIVILEGE")
...
.formLogin().successHandler(successHandler)
}
decide where the user lands after loginに対してカスタム認証成功ハンドラーを使用しています。
@Component
public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(
HttpServletRequest request, HttpServletResponse response, Authentication auth)
throws IOException, ServletException {
Set privieleges = AuthorityUtils.authorityListToSet(auth.getAuthorities());
if (privieleges.contains("ADMIN_READ_PRIVILEGE")) {
response.sendRedirect("adminHome");
} else {
response.sendRedirect("home");
}
}
}
そして、非常にシンプルな管理者ホームページadminHome.html:
Welcome, Bob
Display Users List
9. 結論
ケーススタディのこの新しい部分では、いくつかの簡単なセキュリティアーティファクトをアプリに追加しました–ロールと特権。 このサポートにより、we built two simple features –標準ユーザーのスケジューリング制限と管理ユーザーの最低限の管理者。