90-2.自作の認証手形発行所(AuthenticationProvider)を使用するサンプル(Security3.x系)
概要
Spring Security 3.1系のサンプルです。
自作したクラスを使いたいときにどうすればよいか?
そのサンプルと思っていください。
ただし、フィルタはやり方が少し違うので、フィルタ以外のカスタマイズサンプルと思っていただければと思います。
サンプル全体の説明もお読みください。
目標
まずはゴールを示します。
内容は2.x系の記事と同じです。そちらを参照ください。
また、各クラスの説明も2.x系の記事と同じなので省略しています。
【補足】
実は、ログイン画面からID,PW以外の情報をSpringSecurityに送る方法はもう一つあります。(参考リンク)
Spring Security3.x系から使用できるようになったフィルタで、UsernamePasswordAuthenticationFilterがあります。
これを使用すると、自作したAuthenticationに値を設定できます。
AuthenticationはID、PWなどの情報を保管するクラスで、認証OKになれば認証手形として役割を果たします。
認証前はログイン情報を保管するクラスとしても使用されます。
ここではUsernamePasswordAuthenticationFilterは使用せずに、POSTパラメタの1つにマージする方法を使用しています。
実際のサンプル
自作の認証手形発行所(指紋認証)クラス(/src/com/my/security/FingerAuthenticationProvider.java)
package com.my.security;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.util.StringUtils;
public class FingerAuthenticationProvider implements AuthenticationProvider {
static Log log = LogFactory.getLog(FingerAuthenticationProvider.class);
static public final String PASSWORD_DELIMITER = ":";
private UserDetailsService userDetailsService;
@Override
public Authentication authenticate(Authentication auth)
throws AuthenticationException {
log.debug("独自authenticateを通っています");
//パスワードを展開してリクエストの各種値を取得する
String id = auth.getName();
String complexPasswd = auth.getCredentials().toString();
//チャレンジとパスワードに展開する
int pos = complexPasswd.indexOf(PASSWORD_DELIMITER);
if(!StringUtils.hasText(complexPasswd) || pos < 0){
throw new BadCredentialsException("認証エラー発生");
}
String challenge = complexPasswd.substring(0, pos);
String passwd = complexPasswd.substring(pos+1);
if(!StringUtils.hasText(challenge)){
log.debug("challengeが空です。");
throw new BadCredentialsException("認証エラー発生");
}
//認証
if(fingerAuthenticate(id, passwd, challenge)){
auth = createAuthentication(id, (UsernamePasswordAuthenticationToken) auth);
}else{
throw new BadCredentialsException("認証エラー発生");
}
return auth;
}
protected boolean fingerAuthenticate(String id, String passwd, String challenge){
//実際には指紋認証の処理を記述する
return true;
}
@Override
public boolean supports(Class<?> token) {
return UsernamePasswordAuthenticationToken.class.isAssignableFrom(token);
}
/*
* 認証手形を発行する(作り直す)
* たいていの場合、指紋認証はロールの情報を持っていない。
* DBにIDとロールを登録しているので、DBなどから情報を取得する処理をする。
*/
protected final Authentication createAuthentication(String username, UsernamePasswordAuthenticationToken token) throws AuthenticationException
{
UsernamePasswordAuthenticationToken auth;
UserDetails loadedUser;
try{
loadedUser = getUserDetailsService().loadUserByUsername(username);
}catch (DataAccessException repositoryProblem) {
throw new AuthenticationServiceException(repositoryProblem.getMessage(), repositoryProblem);
}
if (loadedUser == null) {
throw new AuthenticationServiceException("UserDetailsService returned null, which is an interface contract violation");
}
//指紋認証のため特にパスワードは不要。
auth = new UsernamePasswordAuthenticationToken(token.getPrincipal(), "dummy passwd", token.getAuthorities());
auth.setDetails(loadedUser);
return auth;
}
public UserDetailsService getUserDetailsService() {
return userDetailsService;
}
public void setUserDetailsService(UserDetailsService userDetailsService) {
this.userDetailsService = userDetailsService;
}
}
2.x系からの変更点は、パッケージ名が変わっています。
また、BadCredentialsExceptionのコンストラクタの引数が変わりました。
Spring設定ファイル(/WEB-INF/spring/applicationContext-security-finger.xml)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:sec="http://www.springframework.org/schema/security"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.1.xsd">
<!-- 指紋認証の簡易版 -->
<!-- SpringSecurityのメイン設定
-->
<sec:http access-denied-page="/403.jsp" path-type="ant" auto-config="false">
<sec:intercept-url pattern="/error.jsp" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
<sec:intercept-url pattern="/login.jsp" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
<sec:intercept-url pattern="/403.jsp" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
<sec:intercept-url pattern="/**" access="IS_AUTHENTICATED_FULLY"/>
<sec:form-login login-page="/login.jsp" default-target-url="/top.jsp"
authentication-failure-url="/login.jsp?error=true"/>
<sec:logout logout-url="/logout" logout-success-url="/login.jsp" invalidate-session="true"/>
<sec:anonymous granted-authority="ROLE_ANONYMOUS"/>
</sec:http>
<!-- 認証処理 -->
<sec:authentication-manager>
<sec:authentication-provider ref="fingerAuthenticationProvider" />
</sec:authentication-manager>
<!-- 指紋認証の認証手形発行所 -->
<bean id="fingerAuthenticationProvider" class="com.my.security.FingerAuthenticationProvider">
<property name="userDetailsService" ref="userService"/>
</bean>
<!--
ログインユーザの設定。AuthenticationのUserに設定される。
自作Providerが独自の認証を行うためpasswordは実際には使用されない。
-->
<sec:user-service id="userService">
<sec:user name="taro" password="taro" authorities="ROLE_ADMIN"/>
<sec:user name="hanako" password="hanako" authorities="ROLE_READ"/>
</sec:user-service>
</beans>
他のサンプルでもみましたが、authentication-providerタグの使い方が変わっています。
authentication-managerタグの中で子タグとして使用するように変わりました。
ログイン画面サンプル(/login.jsp)
<%@ page language="java" pageEncoding="utf-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="security" uri="http://www.springframework.org/security/tags" %>
<%
out.print(session.getId());
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<script type="text/javascript">
//<!--
function submitTest(){
var challenge = document.getElementById("challenge").value;
var token = document.getElementById("token").value;
var complexPasswd = challenge + ":" + token;
alert(complexPasswd);
document.getElementById("passwd").value = complexPasswd;
document.getElementById("f").submit();
return true;
}
//-->
</script>
<title>ログイン</title>
</head>
<body>
<div class="explanation">
<c:if test="${param.error eq 'true'}">
<font color="red"> 入力されたユーザID、パスワードは不正です。<br>
</font>
</c:if> ログインIDを入力してください。<br>
本当はhiddenにするべきものも、動きが分かるように表示してます。</div>
<form id="f" action="<c:url value='j_spring_security_check'/>" method="post"
>
challenge:<input type="text" id="challenge" name="challenge" value="faweorq23p4rjtq3" disabled><br>
token:<input type="text" id="token" name="token" value="AB3B0A00000000000" disabled><br>
<table class="login">
<tr>
<th>ログインID</th>
<td><input type="text" name="j_username" value="<c:out value="${SPRING_SECURITY_LAST_USERNAME}"/>"></td>
</tr>
<tr>
<th>ログインPW</th>
<td><input type="text" id="passwd" name="j_password" ></td>
</tr>
</table>
<input type="button" name="login" value="ログイン" onclick="javascript: submitTest();">
</form>
</body>
</html>
2.x系から特に変更しているものはありません。
Created Date: 2013/07/07