小さなパートナー(https://github.com/lenve/vhr)を行うためのマイクロ担当者は、そのログインマイクロ人員で非常に特別な要求を発見したはずです。
POST要求は、ログオン要求が、データ伝送フォーマットの形式のキー/値です。プロジェクト全体は、これだけではPOSTリクエスト、他のPOSTリクエストデータはJSON形式であるました。
なぜ、のようなこの外観を作りますか?または怠惰な詠唱。
春のセキュリティデフォルトのログインデータフォーマットは、キーの形式/値なので、変更するのが面倒となっています。つい最近ここに下を調整するための時間を取るために、春のセキュリティを記録し、その前端と後端を統一することができるようになります。
さて、私たちは一緒に達成する方法を見て。
1.サーバーインターフェース調整
私たちが知っているすべての、ユーザがログインのユーザ名/パスワードの最初UsernamePasswordAuthenticationFilter
次のようにクラスを処理し、具体的な処理のコードは次のとおりです。
public Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse response) throws AuthenticationException {
String username = obtainUsername(request);
String password = obtainPassword(request);
//省略
}
protected String obtainPassword(HttpServletRequest request) {
return request.getParameter(passwordParameter);
}
protected String obtainUsername(HttpServletRequest request) {
return request.getParameter(usernameParameter);
}
春のセキュリティデフォルトのログインパラメータはキー形式/値を通過するように、なぜこのコードから、我々は見ることができ、そしてのでそれが処理する方法でrequest.getParameterです。
我々はJSONとして定義する必要がありますので、アイデアは非常に単純ですが、場所にカスタムフィルタを定義することですUsernamePasswordAuthenticationFilter
取得パラメータは、ライン上の別の言い方をするときに、と。
ノートへの追加のポイントは、当社の担当者はマイクロコード検証機能が一緒に処理するための検証コードと、そのカスタムフィルタあれば、残っているということであります。
2. [カスタムフィルタ
次はの場所からフィルタを定義しUsernamePasswordAuthenticationFilter
、以下、:
public class LoginFilter extends UsernamePasswordAuthenticationFilter {
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
if (!request.getMethod().equals("POST")) {
throw new AuthenticationServiceException(
"Authentication method not supported: " + request.getMethod());
}
String verify_code = (String) request.getSession().getAttribute("verify_code");
if (request.getContentType().equals(MediaType.APPLICATION_JSON_VALUE) || request.getContentType().equals(MediaType.APPLICATION_JSON_UTF8_VALUE)) {
Map<String, String> loginData = new HashMap<>();
try {
loginData = new ObjectMapper().readValue(request.getInputStream(), Map.class);
} catch (IOException e) {
}finally {
String code = loginData.get("code");
checkCode(response, code, verify_code);
}
String username = loginData.get(getUsernameParameter());
String password = loginData.get(getPasswordParameter());
if (username == null) {
username = "";
}
if (password == null) {
password = "";
}
username = username.trim();
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
username, password);
setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
} else {
checkCode(response, request.getParameter("code"), verify_code);
return super.attemptAuthentication(request, response);
}
}
public void checkCode(HttpServletResponse resp, String code, String verify_code) {
if (code == null || verify_code == null || "".equals(code) || !verify_code.toLowerCase().equals(code.toLowerCase())) {
//验证码不正确
throw new AuthenticationServiceException("验证码不正确");
}
}
}
私たちは、基本的には公式が提供するこのロジック真似UsernamePasswordAuthenticationFilter
の書き込みには、私が説明し、あなたを少しあげます:
- すべてのログオン要求の最初はそうでない場合は、例外ではなく、契約の裏POST、直接スローPOSTでなければなりません。
- ここに起因する処理コードに、第二段階からのセッションの値を確認コードを介して配布されたように。
- ない場合、super.attemptAuthenticationメソッドが呼び出され、パラメータはJSONを渡された場合の方法を解析JSONに応じて、パラメータを渡すJSONによって、現在の要求かどうかを決定するためのcontentType続いて、処理ロジックは、親クラスに、と言うことです私たちカスタムクラス、両方のJSONがパラメータとして渡され、また、パラメータとして渡されたキー/値をサポートしてサポートしています。
- データはJSONの形式である場合、我々はI / Oストリームを読み取ることによって、要求、JSONを地図にマッピングされました。
- 地図から取られたコードは、外出先では、間違ったコードは、直接例外をスローした場合、認証コードは、正しいか否かが判定されます。決定論理検証コード、あなたはを参照することができます:歌Geをどのようにマイクロ職員へのログイン認証コードを追加する方法を教えて。
- 次に、地図からユーザー名とパスワードを削除し、オブジェクトUsernamePasswordAuthenticationTokenをチェックするために構成します。
フィルタの定義が完了したら、我々は代わりにデフォルトのフィルターから、次の定義を使用しUsernamePasswordAuthenticationFilter
、我々は最初の必要性はLoginFilterの例を提供します:
@Bean
LoginFilter loginFilter() throws Exception {
LoginFilter loginFilter = new LoginFilter();
loginFilter.setAuthenticationSuccessHandler(new AuthenticationSuccessHandler() {
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
response.setContentType("application/json;charset=utf-8");
PrintWriter out = response.getWriter();
Hr hr = (Hr) authentication.getPrincipal();
hr.setPassword(null);
RespBean ok = RespBean.ok("登录成功!", hr);
String s = new ObjectMapper().writeValueAsString(ok);
out.write(s);
out.flush();
out.close();
}
});
loginFilter.setAuthenticationFailureHandler(new AuthenticationFailureHandler() {
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
response.setContentType("application/json;charset=utf-8");
PrintWriter out = response.getWriter();
RespBean respBean = RespBean.error(exception.getMessage());
if (exception instanceof LockedException) {
respBean.setMsg("账户被锁定,请联系管理员!");
} else if (exception instanceof CredentialsExpiredException) {
respBean.setMsg("密码过期,请联系管理员!");
} else if (exception instanceof AccountExpiredException) {
respBean.setMsg("账户过期,请联系管理员!");
} else if (exception instanceof DisabledException) {
respBean.setMsg("账户被禁用,请联系管理员!");
} else if (exception instanceof BadCredentialsException) {
respBean.setMsg("用户名或者密码输入错误,请重新输入!");
}
out.write(new ObjectMapper().writeValueAsString(respBean));
out.flush();
out.close();
}
});
loginFilter.setAuthenticationManager(authenticationManagerBean());
loginFilter.setFilterProcessesUrl("/doLogin");
return loginFilter;
}
我々は交換した場合UsernamePasswordAuthenticationFilter
、フォーム上の元の設定フォームがSecurityConfig#のconfigureメソッドに失敗した後、それらの属性の失敗は、時間の設定LoginFilter例で設定することができます。
さらに構成AuthenticationManagerを思い出し、それが構成WebSecurityConfigurerAdapterに応じて提供することができます。
FilterProcessUrlが設定されていない場合、デフォルトは、実際の状況に応じて設定することができます/login
。
最後に、我々は代わりにカスタムLoginFilterの例を使用しUsernamePasswordAuthenticationFilter
、以下のように、:
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
...
//省略
http.addFilterAt(loginFilter(), UsernamePasswordAuthenticationFilter.class);
}
完全な交換作業にaddFilterAtメソッドを呼び出します。
:宇宙の理由は、私は完全なコードの小さなパートナーはGitHubの上で見つけることができ、ここでの唯一のショーのコードの一部だhttps://github.com/lenve/vhr。
設定が完了したら、次のように、最初のPOSTMANテストログインインターフェイスで、バックエンドを再起動します。
3.変更フロントエンド
私たちは、このようなフロントエンドのログインコードを持っていました:
this.$refs.loginForm.validate((valid) => {
if (valid) {
this.loading = true;
this.postKeyValueRequest('/doLogin', this.loginForm).then(resp => {
this.loading = false;
//省略
})
} else {
return false;
}
});
まず、私たちは、検証が成功した後にデータを確認するために行ってきました、postKeyValueRequest法によるログイン要求を送信し、この方法は、以下のように、私自身のパッケージを形成するキー/値によってパラメータを渡すためにPOSTリクエストです:
export const postKeyValueRequest = (url, params) => {
return axios({
method: 'post',
url: `${base}${url}`,
data: params,
transformRequest: [function (data) {
let ret = '';
for (let i in data) {
ret += encodeURIComponent(i) + '=' + encodeURIComponent(data[i]) + '&'
}
return ret;
}],
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
});
}
export const postRequest = (url, params) => {
return axios({
method: 'post',
url: `${base}${url}`,
data: params
})
}
パラメータのキー/値の形式を渡すことによって、I postKeyValueRequestパッケージは、postRequestパラメータはJSONとして渡されます。
だから、私たちは次のように、わずかな調整要求のフロントエンドに署名する必要があります。
this.$refs.loginForm.validate((valid) => {
if (valid) {
this.loading = true;
this.postRequest('/doLogin', this.loginForm).then(resp => {
this.loading = false;
//省略
})
} else {
return false;
}
});
設定後、ログイン、ブラウザプレスF12に行く、あなたはログイン要求の形式でパラメータを見ることができます:
まあ、これは歌のGeであり、サインインをSpringSecurity + JSON +コードをご紹介し、私はかなり良いを感じた場合、私は右下隅タップケインに覚えています。
小さなパートナーの完全なコードはGitHubのからダウンロードできます。https://github.com/lenve/vhr