XMLのないSpring。 パート2

そしてまた、良い一日。 XMLを使用せずにSpring + Java EE + Persistenceの公開を継続するための投稿 パート1」



内容

1.はじめに



1.1プロジェクトをロードします



1.2このパートでは何をしますか?



2.ユーザー間のロールの分配を修正する



2.1データベースでの作業



2.2コードを書く



3. UsersControllerを作成する



3.1新しいユーザーの作成を実装します



3.2特定のユーザーに作業を追加する



4.完成したプロジェクトを開始したい人向け



5結論





1.はじめに



1.1プロジェクトをロードします



この部分から始めたい場合、または前の部分にプロジェクトが残っていない場合は、githubからダウンロードできます。

スキームは単純です:



1.2このパートでは何をしますか?



このパートでは、エンティティオブジェクトのレベルで多対多のリレーションシップがどのように格納されるかを見ていきます。

ユーザーへの権利の分配を完了します。

最も単純なRESTコントローラーを作成します。

新規ユーザーの登録(管理者のみ);

これらはすべてXMLなしです。



2ユーザー間のFiximロールの配布



おそらく誰かが気づいたように、現時点では、データベースからユーザー権限を取得する代わりに、不気味なスタブを添付していますが、これは絶対に柔軟性がありません。



これを修正するには何が必要ですか? そのようなエンティティを「ロール」として導入します。 1人のユーザーが複数のロールを持つことができ、多くのユーザーが同じロールを持つことができます。 つまり クラシック多対多



2.1データベースでの作業



まず、ロール(id、ロール)のラベルを取得します。ロールの値が一意でなければならないことを忘れないでください。 また、補助テーブルusers_roles(user_id、role_id)を作成します。 そして、すぐに基本的な役割ADMIN、USER、GUESTを作成します。 さて、リンクページの外部キーuser_id-> user.id、role_id-> role.idをすぐに作成します。 このすべては、次のスクリプトを実行することですぐに実行できます。



走るだけ
#   roles # ------------------------------------------------------------ DROP TABLE IF EXISTS `roles`; CREATE TABLE `roles` ( `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, `role` varchar(250) DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `id` (`id`), UNIQUE KEY `role` (`role`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; INSERT INTO `roles` (`id`, `role`) VALUES (1,'ADMIN'), (3,'GUEST'), (2,'USER'); #   users # ------------------------------------------------------------ DROP TABLE IF EXISTS `users`; CREATE TABLE `users` ( `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, `username` varchar(250) DEFAULT NULL, `password` varchar(250) DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `id` (`id`), UNIQUE KEY `username` (`username`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; INSERT INTO `users` (`id`, `username`, `password`) VALUES (1,'user','user'); #   users_roles # ------------------------------------------------------------ DROP TABLE IF EXISTS `users_roles`; CREATE TABLE `users_roles` ( `user_id` bigint(20) unsigned DEFAULT NULL, `role_id` bigint(20) unsigned DEFAULT NULL, KEY `hasuser` (`user_id`), KEY `hasrole` (`role_id`), CONSTRAINT `hasrole` FOREIGN KEY (`role_id`) REFERENCES `roles` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT `hasuser` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
      
      









2.2コードを書く



まず、Application.classに移動して、JPAリポジトリの場所を指定します。



 @EnableJpaRepositories(basePackages = {"habraspring.repositories"})
      
      





エンティティ/クラスロールにデータベースへのマップ書き込みを作成します。



Role.java
 @Entity @Table(name="roles") public class Role { @Id @GeneratedValue(strategy = GenerationType.AUTO) private long id; private String role; public long getId() { return id; } public void setId(long id) { this.id = id; } public String getRole() { return role; } public void setRole(String role) { this.role = role; } protected Role(){} public Role(String name) { role = name; } }
      
      







さて、そして今それらをUserクラスにリンクする方法は? これを行うには、次のコードをユーザーに追加します。



  @ManyToMany @JoinTable(name = "users_roles", joinColumns = {@JoinColumn(name = "user_id")}, inverseJoinColumns = @JoinColumn(name = "role_id")) private Set<Role> roles; public Set<Role> getRoles() { return roles; } public void setRoles(Set<Role> roles) { this.roles = roles; }
      
      





ここでは、2つのエンティティの関係のテーブルを示します。このテーブルのどの列がエンティティ(joinColumns = {@JoinColumn(name = "user_id")})に対応し、どの列がリンクされたエンティティに対応します。



Roleクラスでは、すべてがよりシンプルです。



  @ManyToMany(mappedBy = "roles") Set<User> users; public Set<User> getUsers() { return users; } public void setUsers(Set<User> users) { this.users = users; }
      
      





SpringがAuthorithyとしての役割をアレンジするには、RoleクラスにGrantedAuthorityインターフェイスを実装する必要があります。



 public class Role implements GrantedAuthority { ... @Override public String getAuthority() { return getRole(); } }
      
      





できた! これで、MySQLUserDetailsS​​erviceを書き換えることができます。



 @Service public class MySQLUserDetailsService implements UserDetailsService { @Autowired UsersRepository users; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { UserDetails loadedUser; try { User client = users.findByUsername(username); loadedUser = new org.springframework.security.core.userdetails.User( client.getUsername(), client.getPassword(), client.getRoles()); } catch (Exception repositoryProblem) { throw new InternalAuthenticationServiceException(repositoryProblem.getMessage(), repositoryProblem); } return loadedUser; } }
      
      





ここで、ガベージクラスではなくuser.getRoles()を介して権限をロードするため、ユーザーはデータベースで自分に割り当てられているロールのみを受け取ります。



現時点では使用していませんが、少し後で、ユーザーの役割に応じてアクセスを区別する方法がわかります。



そのため、管理者権限を持つユーザーのみがアクセスできる、ユーザーと作業するための単純な休憩コントローラーを作成します。



3. UsersControllerを作成する



最初に、コントローラーフォルダーを作成し、その中にUsersContollerフォルダーを作成します。



 package habraspring.controllers; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/users") public class UsersController { }
      
      





そのため、まず、これはレストコントローラーであることに注意しました。つまり、デフォルトではJson形式で完全なhtmlページではなく、生データが返されます。 @RequestMapping( "/ users")-これは、 " yoursite / users "という形式のユーザーからのリクエストによってトリガーされることを意味します 。 しかし、ここではユーザーを操作しますが、承認されたユーザーはこのコントローラーを開くことができます! そのため、魔法の行を追加します。



 @PreAuthorize("hasRole('ADMIN')")
      
      





コントローラー内では、権限の確認はもう必要ありません。すでにADMINロールを持っている人だけがアクセスできます。



すべてのユーザーの出力を追加します。



  @Autowired UsersRepository users; @RequestMapping(method = RequestMethod.GET) public List<User> getUsers() { List<User> result = new ArrayList<>(); users.findAll().forEach(result::add); return result; }
      
      





アドレスhttp:// localhost:8080 / usersに移動してみてください。すべてが正しく行われた場合、403エラーが発生します。管理者権限をユーザーに追加します。これを行うには、同じユーザーIDと私のADMINロール。 テーブルに新しい値を追加した後、 http:// localhost:8080 / secretに移動し、そこでログアウトをクリックして再ログインします(新しい権限をロードします)。 http:// localhost:8080 / usersを開いてみてください。 次のようなものが出力されます。



多くの手紙
[{"Id":1、 "username": "user"、 "password": "user"、 "roles":[{"id":1、 "role": "ADMIN"、 "users":[{ 「Id」:1、「username」:「user」、「password」:「user」、「roles」:[{"id":1、「role」:「ADMIN」、「users」:[{"id ":1、"ユーザー名 ":"ユーザー "、"パスワード ":"ユーザー "、"役割 ":[{" id ":1、"役割 ":" ADMIN "、"ユーザー ":[{" id ": 1、「ユーザー名」:「ユーザー」、「パスワード」:「ユーザー」、「ロール」:[{"id":1、 "ロール": "ADMIN"、 "ユーザー":[{"id":1 「ユーザー名」:「ユーザー」、「パスワード」:「ユーザー」、「ロール」:[{"id":1、 "ロール": "ADMIN"、 "ユーザー":[{"id":1、 "ユーザー名":"ユーザー "、"パスワード ":"ユーザー "、"ロール ":[{" id ":1、"ロール ":" ADMIN "、"ユーザー ":[{" id ":1、"ユーザー名 ": 「ユーザー」、「パスワード」:「ユーザー」、「ロール」:[{"id":1、 "ロール": "ADMIN"、 "ユーザー":[{"id":1、 "ユーザー名": "ユーザー"、"パスワード ":"ユーザー "、"ロール ":[{" id ":1、"ロール ":" ADMIN "、" users ":[{" id ":1、"ユーザー名 ":" user "、 「パスワード」:「ユーザー」、「ロール」:[{"id":1、 "ロール": "ADMIN"、 "ユーザー":[{"id":1、 "ユーザー名": "ユーザー"、 "パスワード「:」ユーザー」、 「ロール」:[{"id":1、 "ロール": "ADMIN"、 "ユーザー":[{"id":1、 "ユーザー名": "ユーザー"、 "パスワード": "ユーザー"、 "ロール":[{" Id ":1、"ロール ":" ADMIN "、"ユーザー ":[{" id ":1、"ユーザー名 ":"ユーザー "、"パスワード ":"ユーザー "、"ロール ": [{"Id":1、 "role": "ADMIN"、 "users":[{"id":1、 "username": "user"、 "password": "user"、 "roles":[{ 「Id」:1、「role」:「ADMIN」、「users」:[{"id":1、「username」:「user」、「password」:「user」、「roles」:[{"id ":1、"ロール ":" ADMIN "、"ユーザー ":[{" id ":1、"ユーザー名 ":"ユーザー "、"パスワード ":"ユーザー "、"ロール ":[{" id ": 1、「ロール」:「ADMIN」、「ユーザー」:



問題は、結局のところ、ユーザーが1人しかいないということですか? ここではすべてが簡単です。jsonとしてのオブジェクトの自動出力では、そのすべてのフィールドも表示されるため、結果としてそのうちの1つに含まれている場合、無限ループになります。 厄介な見落としを修正するには、RoleクラスのusersフィールドにJsonIgnore注釈を追加します。



 import com.fasterxml.jackson.annotation.JsonIgnore; ... @JsonIgnore @ManyToMany(mappedBy = "roles") Set<User> users;
      
      





アプリケーションを再起動し、 http:// localhost:8080 / usersに移動すると、通常の出力が表示されます。



 [{"id":1,"username":"user","password":"user","roles":[{"id":1,"role":"ADMIN","authority":"ADMIN"}]}]
      
      





では、新しいユーザーを作成するために、さらに2つのメソッドと「ユーザーフレンドリー」なインターフェイスを追加します。



3.1新しいユーザーの作成を実装します



これを行うには、まず、POST要求によって新しいエンティティを追加するメソッドを実装します。

 @RequestMapping(method = RequestMethod.POST) public User addUser(String username, String password, String password_confirm) { //no empty fields allowed if (username.isEmpty() || password.isEmpty() || password_confirm.isEmpty()) return null; //passwords should match if (!password.equals(password_confirm)) return null; return users.save(new User(username, password)); }
      
      







すでに悪くはありませんが、サードパーティのAPIからユーザーをリクエストして投稿を追加できるようになりましたが、アプリケーションでこれを行う方法は?



これを行うには、GET / users / addのリクエストに応じて実行されるネストされたルート/ addを作成します。



  @RequestMapping(value = "/add",method = RequestMethod.GET) public ModelAndView getUserForm() { return new ModelAndView("add"); }
      
      





そして、リソース/テンプレート/add.htmlに追加します:



 <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3"> <head> <title>Add user page</title> </head> <body> <form th:action="@{/users}" method="post"> <div><label> User Name : <input type="text" name="username"/> </label></div> <div><label> Password: <input type="password" name="password"/> </label></div> <div><label> Password confirm: <input type="password" name="password_confirm"/> </label></div> <div><input type="submit" value="Sign In"/></div> </form> </body> </html>
      
      





フォームフィールドは、同じ名前のユーザー作成関数のパラメーターに直接変換されます。これは、私の意見では非常に便利です。



テンプレートを@RestControllerに直接出力することはできないため、名前ビュー(.htmlなし)を渡すだけの補助クラスModelAndViewを使用します。



完了しました。残りを使用するか、/ users / addのフォームを使用して、サイトに直接新しいユーザーを作成できます。



3.2特定のユーザーに作業を追加する



特定のユーザーを発行/削除する2つの単純なメソッド(GET / DELETE / users / 2タイプのリクエスト)を追加することは残ります。



  @RequestMapping(value = "/{id}", method = RequestMethod.DELETE) public void delete(@PathVariable("id") Long id) { users.delete(id); } @RequestMapping(value = "/{id}", method = RequestMethod.GET) public User getUser(@PathVariable("id") Long id) { return users.findOne(id); }
      
      





私の意見では、これらの方法は自己文書化されています。 アノテーションPathVariable( "value")は、テンプレート{value}(この場合は数字)の代わりに、その中にあるものをリクエストから引き出します。



4.完成したプロジェクトを開始したい人向け



https://github.com/MaxPovver/ForHabrahabr/tree/withcontrollerブランチには必要なものがすべて含まれており、最初にデータベースでimport_me.sqlを実行する必要があります(ダウンロード/クローン作成後、チェックアウトすることを忘れないでください)。



5.結論



私はこの記事にもっと多くの記事を載せたかったのですが、私の意見ではすでに少し過負荷になっており、半分にも達していません。 そのため、もちろんトピックに関心がある場合は、テスト、OneToMany、およびいくつかの興味深いことを次の記事に任せなければなりません。



頑張って



All Articles