Open rainit2006 opened 4 years ago
DI: インスタンスを管理する。
@Autowired を付けると、 DIコンテナからインスタンスを取得してくる。
@Scope:インスタンスのライフサイクルを管理する。
singleton コンテナに対して1つのインスタンスを定義します。(デフォルト) prototype Beanを取得する度に新しいインスタンスを生成します。 request HTTP Request単位でインスタンスを生成します。 session HTTP Session単位でインスタンスを生成します。 globalSession something which is connected to Portlet applications. application サーブレットのコンテキスト単位でインスタンスが生成される。Webアプリケーションの場合のみ使用できる。
セキュリティ設定用クラスには、@EnableWebSecurityを付けます。 また、WebSecurityConfigurerAdapterクラスを継承します。
@EnableWebSecurity
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
...
}
直リンクを禁止するためには、http.authorizeRequests()にメソッドチェーンでリンク禁止先の条件を追加していきます。メソッドチェーンとは、.(ドット)でメソッドを連続して呼び出すことです。
@Override
protected void configure(HttpSecurity http) throws Exception {
// ログイン不要ページの設定
http
.authorizeRequests()
.antMatchers("/webjars/**").permitAll() //webjarsへアクセス許可
.antMatchers("/css/**").permitAll() //cssへアクセス許可
.antMatchers("/login").permitAll() //ログインページは直リンクOK
.antMatchers("/signup").permitAll() //ユーザー登録画面は直リンクOK
.antMatchers("/admin").hasAuthority("ROLE_ADMIN") //アドミンユーザーに許可
.anyRequest().authenticated(); //それ以外は直リンク禁止
//CSRF対策を無効に設定(一時的)
//http.csrf().disable();
}
ログイン処理を追加するには、http.formLogin()にメソッドチェーンで条件を追加していきます。
//ログイン処理
http
.formLogin()
.loginProcessingUrl("/login") //ログイン処理のパス
.loginPage("/login") //ログインページの指定
.failureUrl("/login") //ログイン失敗時の遷移先
.usernameParameter("userId") //ログインページのユーザーID
.passwordParameter("password") //ログインページのパスワード
.defaultSuccessUrl("/home", true); //ログイン成功後の遷移先
次に、login.htmlを修正して、ユーザーID、パスワードの入力エリアにname属性を追加します。 また、ログイン失敗時のメッセージを出すように修正します。
ポイント: ログインエラーメッセージの表示ログインエラーメッセージを表示するためには、th:if属性を使います。
メッセージは、messages.propertiesに設定しておきます。
// データソース
@Autowired
private DataSource dataSource;
// ユーザーIDとパスワードを取得するSQL文
private static final String USER_SQL = "SELECT"
+ " user_id,"
+ " password,"
+ " true"
+ " FROM"
+ " m_user"
+ " WHERE"
+ " user_id = ?";
// ユーザーのロールを取得するSQL文
private static final String ROLE_SQL = "SELECT"
+ " user_id,"
+ " role"
+ " FROM"
+ " m_user"
+ " WHERE"
+ " user_id = ?";
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// ログイン処理時のユーザー情報を、DBから取得する
auth.jdbcAuthentication()
.dataSource(dataSource)
.usersByUsernameQuery(USER_SQL)
.authoritiesByUsernameQuery(ROLE_SQL)
.passwordEncoder(passwordEncoder());
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
パスワードを暗号化したり、復号するインターフェースとして、PasswordEncoderというインターフェースがSpringで用意されています。そのPasswordEncoderを実装した、BCryptPasswordEncoderのインスタンスを返すBean定義をしています。
//ログアウト処理
http
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout")) //
.logoutUrl("/logout") //ログアウトのURL
.logoutSuccessUrl("/login"); //ログアウト成功後のURL
<!CSRF対策用トークン>
<inputtype="hidden"
th:name="${_csrf.parameterName}"
th:value="${_csrf.token}"/>
上記のコードでは、hiddenでCSRF対策用のトークンをサーバーに送ります。CSRF対策を有効にしている場合、フォームを使っている画面では上記のコードを追加しないといけません。そうしないと、Springが何も応答しなくなります。
th:action属性を使っていると、タイムリーフでは自動でCSRF対策用のトークンを追加してくれます。本書では、学習のためにログイン画面だけaction属性を使っていました。基本的にはaction属性ではなく、th:action属性を使うようにしましょう。
URLでの認可設定には、以下のメソッドを追加 ・antMatchers("<パス>")アクセス制限するパスを設定します。正規表現も使えます。 ・hasAuthority("<ロール>")ロールには、データベースから取得してきたロール名を指定します。
画面表示の認可をするためには、まずhtmlタグ内に以下のコードを追加します。 xmlns:sec="http://www.thymeleaf.org/thymeleafextrasspringsecurity4" これは、タイムリーフの拡張ライブラリを使用する、ということを表しています。
そして、権限によって表示・非表示を切り替えたい箇所に、以下のようなコードを追加します。 認可用のコード sec:authorize="hasRole('<権限>')"
<htmlxmlns:th="http://www.thymeleaf.org"xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"xmlns:sec="http://www.thymeleaf.org/thymeleaf-extrass-pringsecurity4">
<!ポイント:画面表示の認可>
<lirole="presentation"sec:authorize="hasRole('ADMIN')">
<ath:href="@{'/admin'}">アドミン用画面</a>
</li>
RESTFUL
REST用コントローラーREST用コントローラーを作成するには、クラスに@RestControllerを付ける。@RestControllerを付けると、そのクラスのメソッドの戻り値を呼び出し元に返す。 PUTメソッドには、@PutMappingを使う。 DELETEメソッドには、@DeleteMappingを使う。
基本流程: Controller-》Service-》DB
SpringTestとは、Springを起動した後にJUnitなどのテストを実行できるフレームワークです。SpringTestを使うには、以下の2つのアノテーションをクラスに付けます @RunWith(SpringRunner.class) @SpringBootTest
ポイント1: テスト用アノテーションテスト用のクラスには、@RunWith(SpringRunner.class)と@SprintBootTestの2つのアノテーションを付けます。 ・@RunWith(SpringRunner.class) @RunWithアノテーションは、テストをどのクラスで実行するか指定できます。SpringRunnerクラスは、Spring用のJUnitを使えるクラスです。 ・@SprintBootTestこのアノテーションを付けると、SpringBootを起動してからテストを始めてくれます。
@RunWith(SpringRunner.class)
@SpringBootTest
@Transactional
public class UserDaoTest {
@Autowired
@Qualifier("UserDaoJdbcImpl")
UserDao dao;
// カウントメソッドのテスト1
@Test
public void countTest1() {
// カウントメソッドの結果が2件であることをテスト
assertEquals(dao.count(), 2);
}
// カウントメソッドのテスト2
@Test
@Sql("/testdata.sql")
public void countTest2() {
// カウントメソッドの結果が3件であることをテスト
assertEquals(dao.count(), 3);
}
}
補足:staticインポートJUnitを使用する際は、コードの記述量を少なくしたり、可読性を上げるためにstaticインポートがよく使われます。 上記のコードで言うと、1行目のimportstaticorg.junit.Assert.∗;の部分です。 こうすることで、Assert.assertEquals(…)ではなく、assertEquals(…)だけでメソッドを実行できます。 staticインポートはEclipseが自動で付けてくれないため、ご注意ください。 この後のコードでも使用しています。
【実行】Springテストは、JUnitを起動すればテストが開始されます。やり方は、Alt+Shift+Xの後にTを押します。
ポイント:@Sql @Sqlアノテーションを使用すると、そのSQLを実行した後の状態でテストされます。 ただし、@Sqlに記載されているSQLはそのメソッド内だけで有効です。
//カウントメソッドのテスト2
//ポイント:@Sql
@Test
@Sql("/testdata.sql")
public void countTest2(){
//カウントメソッドの結果が3件であることをテスト
assertEquals(dao.count(),3);
}
ポイント2:画面表示内容の確認 上記のテストコードでは、Springのモックを使って、ログイン画面のテストを行っています。各コードでは、以下のようなテストをしています。 ・mockMvc.perform(get("/login"))/loginにGETリクエストを送っています。つまり、ログイン画面を取得しています。 ・.andExpect(status().isOk())HTTPリクエストが正常に終了したかどうかをチェックしています。 ・.andExpect(content().string(containsString("ユーザーID"))); ログイン画面のhtmlに「ユーザーID」という文字が含まれているかをチェックしています。
@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class LoginControllerTest {
@Autowired
private MockMvc mockMvc;
@Test
public void ログイン画面表示() throws Exception {
//画面表示内容の確認
mockMvc.perform(get("/login"))
.andExpect(status().isOk())
.andExpect(content().string(containsString("ユーザーID")));
}
}
ログインしなければ表示できない画面についてテストします。 HomeControllerTest.java
@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class HomeControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private UserService service;
@Test
@WithMockUser
public void ユーザーリスト画面のユーザー件数のテスト() throws Exception {
// UserServiceのcountメソッドの戻り値を10に設定
when(service.count()).thenReturn(10);
// ユーザーリスト画面のチェック
mockMvc.perform(get("/userList"))
.andExpect(status().isOk())
.andExpect(content().string(containsString("合計:10件")));
}
}
@WithMockUserアノテーションを使うと、ログインした後にしか表示できない画面のテストをすることができます。なお、以下のようにusername,password,roles属性を指定することもできます。 ・ユーザー指定サンプル@WithMockUser(username="satou",roles={"ROLE_ADMIN"})
ポイント2:モックの戻り値設定 Springが用意するMockitoを使って、メソッドの戻り値を任意の値に変更することができます。まずは、モックとして使用するBeanに@MockBeanアノテーションを付けます。 その後、以下のコード部分でメソッドの戻り値を任意の値にしています。 ・when(service.count()).thenReturn(10); これは、service.count()メソッドの戻り値を10にしています。 その後、ユーザー一覧画面の表示内容が変わっていることをテストしています。
@MockBean负责声明这是一个模拟的bean。在进行单元测试时,需要将测试目标的所有依赖bean声明为模拟的bean,这些模拟的bean将被注入测试目标bean。
https://docs.spring.io/spring-security/site/docs/5.0.x/reference/html/test-method.html
@WithMockUser The question is "How could we most easily run the test as a specific user?" The answer is to use @WithMockUser. The following test will be run as a user with the username "user", the password "password", and the roles "ROLE_USER".
@Test
@WithMockUser
public void getMessageWithMockUser() {
String message = messageService.getMessage();
...
}
The following test would run with the username "customUser". Again, the user does not need to actually exist.
@Test
@WithMockUser("customUsername")
public void getMessageWithMockUserCustomUsername() {
String message = messageService.getMessage();
...
}
ポイント1:@Mapper MyBatisでSQLを実行するクラスには@Mapperアノテーションを付けます。
ポイント2:変数の指定 SQLを実行するためには、@Insertや@Selectなどのアノテーションを付けます。 そして、アノテーションの引数にSQL文をセットします。 これが、アノテーションでのMyBatisの使い方になります。 そして、#{<変数名>}と指定することで、SQL文にメソッドの引数をセットできます。
ポイント3:カラム名 Select文を実行して、その戻り値をUserなどの参照型にしているメソッドには、注意が必要です。 それは、テーブルのカラム名と、クラスのフィールド名を一致させなければいけないからです。 ユーザーIDの例で説明すると、以下のようになります。 ・ユーザーID m_userテーブルのカラム名:user_id Userクラスのフィールド名:userId このように、テーブルのカラム名と、クラスのフィールド名が一致していない場合は、SQL文にAS句を使ってカラム名を変更します。 そうすることで、カラム名とフィールド名が一致して、Select結果をUserクラスにセットされます。
MyBatis示例 https://www.youtube.com/watch?v=QR3RnWFZmQ0 (千锋Java教程:33 SpringBoot整合Mybatis注解方式)
创建一个Mapper接口
创建一个Service
再创建一个Controller,实现Restful API相应
@PathVariable: URLに含まれる動的なパラメータを取得
@RequestMapping(value = "/hello/{userName}/{greeting}", method = RequestMethod.GET)
public String printWelcome(@PathVariable("userName") String userName,
@PathVariable("greeting") String greeting,
ModelMap model) {....}
https://app.box.com/s/g51ig6ntwv4pvpypxixsklieoa6oq2an/folder/97611523142