Spring Security実戦乾物:カスタムログインをプレイする
12609 ワード
1.はじめに
前のSpring Securityに関する記事は予熱にすぎない.次のより良い実戦のために、もしあなたが逃したらSpring Security実戦シリーズから始めてください.セキュリティ・アクセスの最初のステップは認証(
2.formログインの流れ
次はformログインの基本手順です.
formログインであれば基本的に上のフローに変換できます.次にSpring Securityがどのように処理されているかを見てみましょう.
3.Spring Securityでのログイン
昨日Spring Security実戦乾物:カスタム構成クラスエントリWebSecurityConfigurerAdapterでは、通常のカスタムアクセス制御は主に に基づく. に基づく.
以上の3つの方式はすべて
4.HttpSecurityでのformフォームログイン
フォーム登録を有効にするには、2つの方法があります.1つは、
4.1 FormLoginConfigurer
このクラスはformフォームログインの構成クラスです.一般的な構成方法をいくつか提供します. です. に代わることができる. に代わることができる. を開放するかどうか
これらを知ったら、カスタマイズされたログインをすることができます.
5.Spring Security集約登録実戦
次は私たちが最も感動的な実戦ログイン操作です.Spring実戦の一連の予熱文章を真剣に読むことができるのは疑問だ.
5.1シンプルなニーズ
我々のインタフェースアクセスは認証に合格し,ログインエラー後にエラー情報(json)を返し,成功後フロントで対応するデータベースユーザ情報(json)を取得することができる(実戦では脱敏を覚えている).
成功に失敗したコントローラを定義します.
次に、カスタム構成上書き
Postmanまたは他のツールを使用してPost方式のフォームを発行すると、
パスワードを別の値に変更して認証を再要求できませんでした.
6.多種の登録方式の簡単な実現
これで終わりですか.現在は登録の種類が多い.通常はメール、メールボックス、スキャンコードがありますが、第三者は後で私が話したいのは今日の範囲内ではありません.アイデアの多いプロダクトマネージャにどう対応しますか?さまざまなポーズを拡張できるログイン方法を作りましょう.私たちは上の2.formログインのプロセスの中のユーザーと判定の間にアダプタを追加して適応すればいいです.この判定とは
6.1ログイン方式の定義
ログイン方式列挙``を定義します.
6.2プリプロセッサインタフェースの定義
プリアンブルプロセッサインタフェースを定義して、受信した様々な特色のあるログインパラメータを処理し、具体的な論理を処理します.この言い訳は実は少し勝手で、重要なのはあなたが考えをマスターすることです.デフォルトの
6.3登録前処理フィルタの実現
このフィルタは、
6.4検証
より多くの方法は、インタフェース
7.まとめ
今日,我々は各種技術の運用により,単純登録から動的拡張まで多様な方式が併存する実戦運用を実現した.あなたにとって大きな収穫があると信じています.今回のコードDEMOは、公衆番号に注目することで、
個人ブログ:https://felord.cn
前のSpring Securityに関する記事は予熱にすぎない.次のより良い実戦のために、もしあなたが逃したらSpring Security実戦シリーズから始めてください.セキュリティ・アクセスの最初のステップは認証(
Authentication
)、認証の最初のステップはログインです.今日はSpring Securityのカスタマイズにより、拡張性と伸縮性のあるformログイン機能を設計します.2.formログインの流れ
次はformログインの基本手順です.
formログインであれば基本的に上のフローに変換できます.次にSpring Securityがどのように処理されているかを見てみましょう.
3.Spring Securityでのログイン
昨日Spring Security実戦乾物:カスタム構成クラスエントリWebSecurityConfigurerAdapterでは、通常のカスタムアクセス制御は主に
HttpSecurity
によって構築されていると述べています.デフォルトでは、次の3つのログイン方法があります.formLogin()
一般フォーム登録oauth2Login()
OAuth2.0
openidLogin()
認証/認証プロトコルOpenID
AbstractAuthenticationFilterConfigurer
アイデンティティ認証仕様以上の3つの方式はすべて
HttpSecurity
で実現され、4.HttpSecurityでのformフォームログイン
フォーム登録を有効にするには、2つの方法があります.1つは、
apply(C configurer)
のAbstractAuthenticationFilterConfigurer
方法によって、比較的高度な遊び方であるHttpSecurity
の実装を独自に構築することです.もう1つは、formLogin()
のFormLoginConfigurer
メソッドを使用してloginPage(String loginPage)
をカスタマイズすることです.まず普通の2つ目をやってみましょう.4.1 FormLoginConfigurer
このクラスはformフォームログインの構成クラスです.一般的な構成方法をいくつか提供します.
/login
:インタフェースではなくページにログインし、前後の分離モードを変更する必要があります.デフォルトはloginProcessingUrl(String loginProcessingUrl)
です.Action
実際のテーブルは、ユーザ情報のUsernamePasswordAuthenticationFilter
を一方的にバックグラウンドに送信し、フィルタAction
によってブロック処理され、このusernameParameter(String usernameParameter)
は実際には論理を処理しない.username
ユーザーパラメータ名をカスタマイズするために使用されます.デフォルトはpasswordParameter(String passwordParameter)
です.password
ユーザーパスワード名をカスタマイズするために使用されます.デフォルトはfailureUrl(String authenticationFailureUrl)
failureForwardUrl(String forwardUrl)
ログインに失敗すると、このパスにリダイレクトされ、一般的に前後の分離では使用されません.Controller
ログインに失敗すると、これに転送され、一般的には前後に分離して使用されます.戻り値を処理するためにRequestMethod
(コントローラ)を定義できますが、defaultSuccessUrl(String defaultSuccessUrl, boolean alwaysUse)
に注意してください.alwaysUse
デフォルトログイン成功後にここにジャンプし、true
がfalse
であれば認証プロセスが成功すれば、ずっとここにジャンプします.一般推奨デフォルト値successForwardUrl(String forwardUrl)
defaultSuccessUrl
の効果は、上のalwaysUse
のtrue
と同等ですが、RequestMethod
に注意してください.successHandler(AuthenticationSuccessHandler successHandler)
カスタム認証成功プロセッサは、上記のすべてのsuccess
方式failureHandler(AuthenticationFailureHandler authenticationFailureHandler)
カスタム失敗成功プロセッサは、上記のすべてのsuccess
方式permitAll(boolean permitAll)
formフォーム登録これらを知ったら、カスタマイズされたログインをすることができます.
5.Spring Security集約登録実戦
次は私たちが最も感動的な実戦ログイン操作です.Spring実戦の一連の予熱文章を真剣に読むことができるのは疑問だ.
5.1シンプルなニーズ
我々のインタフェースアクセスは認証に合格し,ログインエラー後にエラー情報(json)を返し,成功後フロントで対応するデータベースユーザ情報(json)を取得することができる(実戦では脱敏を覚えている).
成功に失敗したコントローラを定義します.
@RestController
@RequestMapping("/login")
public class LoginController {
@Resource
private SysUserService sysUserService;
/**
* 401 .
*
* @return the rest
*/
@PostMapping("/failure")
public Rest loginFailure() {
return RestBody.failure(HttpStatus.UNAUTHORIZED.value(), " , ");
}
/**
* .
*
* @return the rest
*/
@PostMapping("/success")
public Rest loginSuccess() {
// UserDetails SecurityContextHolder
User principal = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
String username = principal.getUsername();
SysUser sysUser = sysUserService.queryByUsername(username);
//
sysUser.setEncodePassword("[PROTECT]");
return RestBody.okData(sysUser," ");
}
}
次に、カスタム構成上書き
void configure(HttpSecurity http)
メソッドを使用して、crsfを無効にする必要があります. @Configuration
@ConditionalOnClass(WebSecurityConfigurerAdapter.class)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
public class CustomSpringBootWebSecurityConfiguration {
@Configuration
@Order(SecurityProperties.BASIC_AUTH_ORDER)
static class DefaultConfigurerAdapter extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
super.configure(auth);
}
@Override
public void configure(WebSecurity web) throws Exception {
super.configure(web);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.cors()
.and()
.authorizeRequests().anyRequest().authenticated()
.and()
.formLogin()
.loginProcessingUrl("/process")
.successForwardUrl("/login/success").
failureForwardUrl("/login/failure");
}
}
}
Postmanまたは他のツールを使用してPost方式のフォームを発行すると、
http://localhost:8080/process?username=Felordcn&password=12345
がユーザー情報を返します. {
"httpStatus": 200,
"data": {
"userId": 1,
"username": "Felordcn",
"encodePassword": "[PROTECT]",
"age": 18
},
"msg": " ",
"identifier": ""
}
パスワードを別の値に変更して認証を再要求できませんでした.
{
"httpStatus": 401,
"data": null,
"msg": " , ",
"identifier": "-9999"
}
6.多種の登録方式の簡単な実現
これで終わりですか.現在は登録の種類が多い.通常はメール、メールボックス、スキャンコードがありますが、第三者は後で私が話したいのは今日の範囲内ではありません.アイデアの多いプロダクトマネージャにどう対応しますか?さまざまなポーズを拡張できるログイン方法を作りましょう.私たちは上の2.formログインのプロセスの中のユーザーと判定の間にアダプタを追加して適応すればいいです.この判定とは
UsernamePasswordAuthenticationFilter
であることを知っています.uri
が上記に構成された/process
であり、getParameter(String name)
を介してユーザ名とパスワードを取得できることを保証するだけでよい.DelegatingPasswordEncoder
のやり方を真似て、レジストリを維持して異なる処理戦略を実行できると思います.もちろん、GenericFilterBean
がUsernamePasswordAuthenticationFilter
の前に実行されることを実現します.同時にログインのポリシーを設定します.6.1ログイン方式の定義
ログイン方式列挙``を定義します.
public enum LoginTypeEnum {
/**
* .
*/
FORM,
/**
* Json .
*/
JSON,
/**
* .
*/
CAPTCHA
}
6.2プリプロセッサインタフェースの定義
プリアンブルプロセッサインタフェースを定義して、受信した様々な特色のあるログインパラメータを処理し、具体的な論理を処理します.この言い訳は実は少し勝手で、重要なのはあなたが考えをマスターすることです.デフォルトの
form'
RequestBody
json`の2つの方法を実現しましたが、ここでは紙幅制限は示しません.具体的なDEMOは下部を参照してください. public interface LoginPostProcessor {
/**
*
*
* @return the type
*/
LoginTypeEnum getLoginTypeEnum();
/**
*
*
* @param request the request
* @return the string
*/
String obtainUsername(ServletRequest request);
/**
*
*
* @param request the request
* @return the string
*/
String obtainPassword(ServletRequest request);
}
6.3登録前処理フィルタの実現
このフィルタは、
LoginPostProcessor
マッピングテーブルを維持します.ログイン方式によるポリシー上の前処理は、フロントエンドで判定され、最終的にはUsernamePasswordAuthenticationFilter
に渡される.HttpSecurity
のaddFilterBefore(preLoginFilter, UsernamePasswordAuthenticationFilter.class)
法により前置を行った. package cn.felord.spring.security.filter;
import cn.felord.spring.security.enumation.LoginTypeEnum;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.web.filter.GenericFilterBean;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import static org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter.SPRING_SECURITY_FORM_PASSWORD_KEY;
import static org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter.SPRING_SECURITY_FORM_USERNAME_KEY;
/**
*
*
* @author Felordcn
* @since 16 :21 2019/10/17
*/
public class PreLoginFilter extends GenericFilterBean {
private static final String LOGIN_TYPE_KEY = "login_type";
private RequestMatcher requiresAuthenticationRequestMatcher;
private Map processors = new HashMap<>();
public PreLoginFilter(String loginProcessingUrl, Collection loginPostProcessors) {
Assert.notNull(loginProcessingUrl, "loginProcessingUrl must not be null");
requiresAuthenticationRequestMatcher = new AntPathRequestMatcher(loginProcessingUrl, "POST");
LoginPostProcessor loginPostProcessor = defaultLoginPostProcessor();
processors.put(loginPostProcessor.getLoginTypeEnum(), loginPostProcessor);
if (!CollectionUtils.isEmpty(loginPostProcessors)) {
loginPostProcessors.forEach(element -> processors.put(element.getLoginTypeEnum(), element));
}
}
private LoginTypeEnum getTypeFromReq(ServletRequest request) {
String parameter = request.getParameter(LOGIN_TYPE_KEY);
int i = Integer.parseInt(parameter);
LoginTypeEnum[] values = LoginTypeEnum.values();
return values[i];
}
/**
* Form .
*
* @return the login post processor
*/
private LoginPostProcessor defaultLoginPostProcessor() {
return new LoginPostProcessor() {
@Override
public LoginTypeEnum getLoginTypeEnum() {
return LoginTypeEnum.FORM;
}
@Override
public String obtainUsername(ServletRequest request) {
return request.getParameter(SPRING_SECURITY_FORM_USERNAME_KEY);
}
@Override
public String obtainPassword(ServletRequest request) {
return request.getParameter(SPRING_SECURITY_FORM_PASSWORD_KEY);
}
};
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
ParameterRequestWrapper parameterRequestWrapper = new ParameterRequestWrapper((HttpServletRequest) request);
if (requiresAuthenticationRequestMatcher.matches((HttpServletRequest) request)) {
LoginTypeEnum typeFromReq = getTypeFromReq(request);
LoginPostProcessor loginPostProcessor = processors.get(typeFromReq);
String username = loginPostProcessor.obtainUsername(request);
String password = loginPostProcessor.obtainPassword(request);
parameterRequestWrapper.setAttribute(SPRING_SECURITY_FORM_USERNAME_KEY, username);
parameterRequestWrapper.setAttribute(SPRING_SECURITY_FORM_PASSWORD_KEY, password);
}
chain.doFilter(parameterRequestWrapper, response);
}
}
6.4検証
POST
フォーム送信方式http://localhost:8080/process?username=Felordcn&password=12345&login_type=0
により、成功を要求することができる.または、次の方法でコミットできます.より多くの方法は、インタフェース
LoginPostProcessor
注入PreLoginFilter
を実現するだけである.7.まとめ
今日,我々は各種技術の運用により,単純登録から動的拡張まで多様な方式が併存する実戦運用を実現した.あなたにとって大きな収穫があると信じています.今回のコードDEMOは、公衆番号に注目することで、
Felordcn
からss03
に返信することができます.後はもっと素晴らしいです. :Felordcn
個人ブログ:https://felord.cn