質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

91.03%

  • Java

    12136questions

    Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

  • Spring Boot

    378questions

    Spring Bootは、Javaのフレームワークの一つ。Springプロジェクトが提供する様々なフレームワークを統合した、アプリケーションを高速で開発するために設計されたフレームワークです。

Spring Securityを拡張したいが、ログインが上手くいかない

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 577

omou

score 5

Spring Securityを拡張して「ユーザー名」「パスワード」のほかに「チームID」という文字列を認証処理の要素として追加したいのですが、ログイン後意図したURLに遷移せずに、ルート「"/"」に遷移して404エラーが発生してしまいます。(ログを見た感じだと認証自体は成功しているように見えます。)

一点気になっているのが、WebSecurityConfigクラスにてaddFilter()の記述を除外すると、意図したURLへ遷移します(ログイン中のユーザー情報を取得できずNullPointerExceptionが発生してしまいますが)

とりあえず具体的なログイン処理は後回しにしてDBから「ユーザー名」「パスワード」「チームID」を取得し、それらをそのまま認証情報として受け取り、ログイン成功させる所まで行きたいです。

以下ソースを記載します。(いろいろなサイトを参考にして試行錯誤しているため、読みづらく無駄な部分があるかと思います。。)
長くなってしまい大変恐縮ですが、宜しくお願い致します。

@EnableWebSecurity
@Component
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    private final Logger logger = LoggerFactory.getLogger(WebSecurityConfig.class);

    // 認証
    @Autowired
    private UserDaoAuthenticationProvider userDaoAuthenticationProvider;
    @Autowired
    private UserDetailsService userDetailsService;

    // 暗号化
    @Autowired
    private BCryptPasswordEncoder bCryptPasswordEncoder;

    @Autowired
    private UserAuthenticationFilter userAuthenticationFilter;

    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        logger.info("WebSecurityConfig configure");

        httpSecurity.csrf();

        httpSecurity.authorizeRequests().antMatchers(LoginController.PAGE).permitAll().anyRequest().authenticated().and()
        .formLogin().loginPage(LoginController.PAGE).successForwardUrl(ProjectManageController.PAGE).defaultSuccessUrl(ProjectManageController.PAGE, false).failureUrl("/error").usernameParameter("user_name").passwordParameter("user_password").permitAll().and().authenticationProvider(userDaoAuthenticationProvider).addFilter(userAuthenticationFilter);
        // Filter消すとdefaultURL反映される
        //.formLogin().loginPage(LoginController.PAGE).successForwardUrl(ProjectManageController.PAGE).defaultSuccessUrl(ProjectManageController.PAGE, false).failureUrl("/error").usernameParameter("user_name").passwordParameter("user_password").permitAll().and().authenticationProvider(userDaoAuthenticationProvider);


    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/resources/**", "/static/**", "/css/**", "/js/**", "/lib/**");
    }

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        logger.info("configureGlobal AuthenticationManagerBuilder start");
        logger.info(auth.toString());
        auth.authenticationProvider(userDaoAuthenticationProvider).userDetailsService(this.userDetailsService).passwordEncoder(bCryptPasswordEncoder);
        logger.info(auth.getDefaultUserDetailsService().toString());
        logger.info("configureGlobal AuthenticationManagerBuilder end");
    }

    @Autowired
    @Override
    protected void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
        logger.info("configure AuthenticationManagerBuilder");
        authenticationManagerBuilder.authenticationProvider(userDaoAuthenticationProvider).userDetailsService(this.userDetailsService).passwordEncoder(bCryptPasswordEncoder);// 入力値をbcryptでハッシュ化した値でパスワード認証を行うように設定
    }

    @Autowired
    public void setUserDetailsService(UserDetailsService userDetailsService) {
        logger.info("setUserDetailsService");
        this.userDetailsService = userDetailsService;
    }
}
package com.omoumou.gantt.web.login;


@Component
public class UserAuthenticationFilter extends UsernamePasswordAuthenticationFilter {

    private static final Logger logger = LoggerFactory.getLogger(UserAuthenticationFilter.class);

    @Autowired
    UserMapper userMapper;

    @Autowired
    private BCryptPasswordEncoder bCryptPasswordEncoder;


    // これがないとだめらしい。フィールド形式のAutowiredだとエラーになる
    @Autowired
    public void setAuthenticationManager(AuthenticationManager authenticationManager) {
        super.setAuthenticationManager(authenticationManager);
    }


    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        logger.info("UserAuthenticationFilter attemptAuthentication");
        if (!request.getMethod().equals("POST")) {
            throw new AuthenticationServiceException("Authentication method not supported: "
                    + request.getMethod());
        }

        setUsernameParameter("user_name");
        setPasswordParameter("user_password");
        String username = super.obtainUsername(request);
        String password = super.obtainPassword(request);
        String teamId = obtainTeamId(request);


        logger.info("debug001:" + request);
        logger.info("debug001:" +request.getHeaderNames());
        logger.info("debug001:" +request.getLocalAddr());
        logger.info("debug001:" + teamId);
        logger.info("debug001:" + username);
        logger.info("debug001:" + password);



        // username required
        if (!StringUtils.hasText(username)) {
            logger.info("UserName is required");
            throw new AuthenticationServiceException("UserName is required");
        }

        LoginUserModel loginUserModel = userMapper.loginUserCheck(username, teamId);

        // 動作確認のためお試し
        if(loginUserModel != null){
            logger.info("debug001: " + loginUserModel.getUserName());
            boolean isLoginSsuccess = bCryptPasswordEncoder.matches(password, loginUserModel.getUserPassword());

            if (!isLoginSsuccess) {
                logger.info("debug001:パスワードが正しくありません。");
                throw new AuthenticationCredentialsNotFoundException("ログイン情報が存在しません。");
            }else{

                logger.info("debug001:パスワードが正しいお");
            }
        }


        UserAuthenticationToken authRequest =  new UserAuthenticationToken(username, password, teamId);

        // Allow subclasses to set the "details" property
        setDetails(request, authRequest);

        return this.getAuthenticationManager().authenticate(authRequest);
    }

    protected String obtainTeamId(HttpServletRequest request) {
        return request.getParameter("team_id");
    }
}
package com.omoumou.gantt.web.login;

public class UserAuthenticationToken extends UsernamePasswordAuthenticationToken {

    private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;

    private static final Logger logger = LoggerFactory.getLogger(UserAuthenticationToken.class);

    //チームID
    private final String teamId;

    public UserAuthenticationToken(Object principal, Object credentials, String teamId) {
        super(principal, credentials);
        this.teamId = teamId;
        logger.info("UserAuthenticationToken 1 ");
    }

    public UserAuthenticationToken(Object principal, Object credentials, String teamId, Collection<? extends GrantedAuthority> authorities) {
        super(principal, credentials, authorities);
        this.teamId = teamId;
        logger.info("UserAuthenticationToken 2 ");
    }

    public String getTeamId() {
        return this.teamId;
    }
}
package com.omoumou.gantt.web.login;

@Component
public class UserDaoAuthenticationProvider extends DaoAuthenticationProvider {

    private static final Logger logger = LoggerFactory.getLogger(UserDaoAuthenticationProvider.class);

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    UserMapper userMapper;

    @Autowired
    private BCryptPasswordEncoder bCryptPasswordEncoder;


    @Autowired
    private UserAuthenticationFilter userAuthenticationFilter;

    // TODO: 以下のメソッドをOverrideしないとUserDetails must be setのエラーが発生してしまう。(継承元のクラスでエラーがでいていた)
    @Override
    protected void doAfterPropertiesSet() throws Exception {
        logger.info("doAfterPropertiesSet start");
        logger.info("doAfterPropertiesSet userDetailsService: " + userDetailsService.getClass());
    }

    @Override
    public Authentication authenticate(Authentication auth) throws AuthenticationException {

        logger.info("authenticate start");

        String username = auth.getName();
        String password = auth.getCredentials().toString();
        logger.info("authenticate username: " + username);
        logger.info("authenticate password: " + password);
        logger.info("authenticate end");

        return new UsernamePasswordAuthenticationToken(auth.getName(), auth.getCredentials(), auth.getAuthorities());
    }

    @Override
    protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
        super.additionalAuthenticationChecks(userDetails, authentication);
        logger.info("additionalAuthenticationChecks start");
        UserAuthenticationToken companyIdUsernamePasswordAuthentication = (UserAuthenticationToken) authentication;
        String requestedCompanyId = companyIdUsernamePasswordAuthentication.getTeamId();
        String companyId = ((LoginUser) userDetails).getUser().getTeamId();

        if (!companyId.equals(requestedCompanyId)) {
            throw new BadCredentialsException(messages.getMessage(
                    "AbstractUserDetailsAuthenticationProvider.badCredentials",
                    "Bad credentials"));
        }
    }

    @Override
    protected Authentication createSuccessAuthentication(Object principal, Authentication authentication, UserDetails user) {
        String companyId = ((LoginUser) user).getUser().getTeamId();
        return new UserAuthenticationToken(user, authentication.getCredentials(), companyId, user.getAuthorities());
    }

    @Override
    public boolean supports(Class<?> token) {
        logger.info("supports");
        return UsernamePasswordAuthenticationToken.class.isAssignableFrom(token);
    }
}
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

+1

フィルターで認証状態(setAuthenticated)で正常(true)を設定したオブジェクトを返却してないからでは?

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/10/12 22:12

    ご回答ありがとうございます。

    this.getAuthenticationManager().authenticate(authRequest).isAuthenticated()
    の結果がtrueとなっているので認証正常となっている認識でいたのですが違うのでしょうか?

    ちなみに
    setAuthenticated(true)
    を記述すると、
    java.lang.IllegalArgumentException: Cannot set this token to trusted
    のエラーが発生してしまいます。

    キャンセル

  • 2017/10/13 00:27

    この実装のだとそうなるのか。

    利用している XxxxxController.PAGE の設定によりけりでわかるかもしれないな。

    isAuthenticated で true が返ってくるならウチの推測は誤りです。

    キャンセル

check解決した方法

0

UserAuthenticationFilterクラスを使用すると(WebSecurityConfigでaddFiltrを設定すると)
、AbstractAuthenticationTargetUrlRequestHandlerクラスのフィールド「defaultTargetUrl」の値が「/」に書き換わっておりました。
※UserAuthenticationFilterクラスを使用しない場合だと、defaultTargetUrlの値は遷移先URLとなっており、ログイン後意図したページへ遷移でる

これはUserAuthenticationFilterクラスの何らかの処理がログイン後遷移先のURLをデフォルトの値に書き換えてしまっていたということなのでしょうか?

とりあえずUserAuthenticationFilterクラスのattemptAuthenticationメソッド内に以下の処理を記述するとログイン後意図したURLに遷移し、ログイン処理が成功することを確認しました。

SavedRequestAwareAuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();
successHandler.setTargetUrlParameter(ProjectManageController.PAGE);
successHandler.setDefaultTargetUrl(ProjectManageController.PAGE);
successHandler.setAlwaysUseDefaultTargetUrl(true);
setAuthenticationSuccessHandler(successHandler);

疑問は残りますが、本件については解決となりますのでクローズとさせていただきます。

お忙しい中ご回答していただき、有難うございました。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 91.03%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • Java

    12136questions

    Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

  • Spring Boot

    378questions

    Spring Bootは、Javaのフレームワークの一つ。Springプロジェクトが提供する様々なフレームワークを統合した、アプリケーションを高速で開発するために設計されたフレームワークです。