まえがき
こちらの記事の続編です。 techhotoke.hatenablog.com
目的
VueとSpringで作成したプロジェクトの構築手順の備忘録。
備忘録のため、詳細な説明を省略している部分があります。
前提
基本的なJavaの知識やSpring、Vueの知識があること。
環境
やること
- 前回はSpring Securityのデフォルト機能しか使っていなかったので、独自認証処理の実装。
- バックエンド側の処理
実装
まずは独自認証用のUserFormクラスを作成します。
UserForm
public class UserForm { private String emailAddress; private String password; public void encrypt(PasswordEncoder encoder){ this.password = encoder.encode(password); } @Override public String toString() { return "UserForm{" + "emailAddress='" + emailAddress + '\'' + ", password='" + password + '\'' + '}'; } }
今回はemailAddressをユーザーIDとして扱います。
次に、独自認証用の設定を行なっていきます。
SpaAuthenticationFilter
@SuppressWarnings("unused") public class SpaAuthenticationFilter extends UsernamePasswordAuthenticationFilter{ private static final Logger LOGGER = LoggerFactory.getLogger(SpaAuthenticationFilter.class); private AuthenticationManager authenticationManager; private BCryptPasswordEncoder bCryptPasswordEncoder; @Autowired private DataSource dataSource; public SpaAuthenticationFilter(AuthenticationManager authenticationManager, BCryptPasswordEncoder bCryptPasswordEncoder) { this.authenticationManager = authenticationManager; this.bCryptPasswordEncoder = bCryptPasswordEncoder; // ログイン用のpathを変更する setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/api/login", "POST")); // ログイン用のID/PWのパラメータ名を変更する setUsernameParameter("emailAddress"); setPasswordParameter("password"); // ログイン後のリダイレクトを抑制 this.setAuthenticationSuccessHandler((req, res, auth) -> res.setStatus(HttpServletResponse.SC_OK)); // ログイン失敗時のリダイレクト抑制 this.setAuthenticationFailureHandler((req, res, ex) -> res.setStatus(HttpServletResponse.SC_UNAUTHORIZED)); } // 認証の処理 @Override public Authentication attemptAuthentication(HttpServletRequest req, HttpServletResponse res) throws AuthenticationException { try { // requestパラメータからユーザ情報を読み取る UserForm userForm = new ObjectMapper().readValue(req.getInputStream(), UserForm.class); return authenticationManager.authenticate( new UsernamePasswordAuthenticationToken( userForm.getEmailAddress(), userForm.getPassword(), new ArrayList<>()) ); } catch (IOException e) { LOGGER.error(e.getMessage()); throw new RuntimeException(e); } } }
WebSecurityConfig
@Configuration @EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Bean public BCryptPasswordEncoder bCryptPasswordEncoder() { return new BCryptPasswordEncoder(); } @Autowired private DataSource dataSource; //ユーザIDとパスワードを取得するSQL文 // 使用可否は全てTRUEで設定 private static final String USER_SQL = "SELECT " + "tmpl_mail AS username, " + "tmpl_password AS password, " + "true " + "FROM tbl_temple " + "WHERE tmpl_mail = ?"; private static final String ROLE_SQL = "SELECT " + "tmpl_mail AS username, " + "role " + "FROM tbl_temple " + "WHERE tmpl_mail = ?"; @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.jdbcAuthentication() .dataSource(dataSource) .usersByUsernameQuery(USER_SQL) .authoritiesByUsernameQuery(ROLE_SQL) .passwordEncoder(bCryptPasswordEncoder()); } @Override protected void configure(HttpSecurity http) throws Exception { http.logout().logoutUrl("/api/logout") .deleteCookies("JSESSIONID") .invalidateHttpSession(true) .logoutSuccessHandler((req, res, auth) -> res.setStatus(HttpServletResponse.SC_OK)) ; http.authorizeRequests() .antMatchers("/").permitAll() .antMatchers("/api/login").permitAll() .antMatchers("/frontend/**").permitAll() .antMatchers("/api/**").authenticated() ; // Spring Securityデフォルトでは、アクセス権限(ROLE)設定したページに未認証状態でアクセスすると403を返すので、 // 401を返すように変更 http.exceptionHandling().authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED)); http.addFilter(new SpaAuthenticationFilter(authenticationManager(), bCryptPasswordEncoder())); http.csrf().ignoringAntMatchers("/api/login").csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()); } }
次に今回はHTML5 History APIを利用したルーティング機能を使用していきたいので、それに関する設定を行なっていきます。 Springのデフォルト設定での静的リソースへのアクセスはトップページ以外はファイル名を拡張子まで指定しなければアクセスできません。 そのため、トップページ以外へ直接アクセスした場合や、画面リフレッシュ時に正しくページが表示できなくなるため、別途設定が必要です。
Html5HistoryModeResourceConfig
/** * 対応しないURLの場合、固定ページを返す。 */ @Configuration public class Html5HistoryModeResourceConfig implements WebMvcConfigurer { private final Resources resourceProperties = new Resources(); @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/**") // 全パスをこのリソースハンドラーの処理対象にする .addResourceLocations(resourceProperties.getStaticLocations()) // 静的リソース配置先のパスを指定する .resourceChain(resourceProperties.getChain().isCache()) // 開発時はfalse、本番はtrueが望ましい。trueにしておくとメモリ上にキャッシュされるためI/Oが軽減される .addResolver(new SpaPageResourceResolver()); // 拡張したPathResourceResolverを読み込ませる } public static class SpaPageResourceResolver extends PathResourceResolver { @Override protected Resource getResource(String resourcePath, Resource location) throws IOException { Resource resource = super.getResource(resourcePath, location); // まずはPathResourceResolverで静的リソースを取得する return resource != null ? resource : super.getResource("/frontend/index.html", location); // 取得できなかった場合は、index.htmlを返す } } }
最後に、ログインフォームからログインが成功した時に返却されるAPIを実装して置きます。 Controllerパッケージ配下にHomeController.javaを作成し、下記のように記述します。
@RestController @RequestMapping("/api") public class HomeController { @GetMapping("/home") @ResponseBody public String getHomeInfomation(){ String text = "Home画面です"; return text; } }
一旦ここまでの設定を行い、http://localhost:8081/frontendにアクセスしてみるとvueのソースへのアクセスなので、設定を変更したため、今は直接アクセスすることが可能になってるはずです。
一旦今回はここまでで、次回はフロントエンド側の実装を行なっていきます。
ここまで読んで頂きありがとうございます。