Можно ли отправить больше данных при аутентификации на основе форм в Spring?
-
06-07-2019 - |
Вопрос
Я относительно новичок в Весенняя структура и Spring безопасность.
Я использовал собственную схему аутентификации HTML:
<form action="j_spring_security_check">
<input type="text" name="j_username" value="abc"/>
<input type="text" name="j_password" value="abc"/>
<input type="text" name="myCustom1" value="pqr"/> <!-- maybe type="hidden" -->
<input type="text" name="myCustom2" value="pqr"/> <!-- maybe type="hidden" -->
</form>
и соответствующий код:
public class CustomAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider
{
@Override protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken)
throws AuthenticationException
{
System.out.println("Method invoked : additionalAuthenticationChecks isAuthenticated ? :"+usernamePasswordAuthenticationToken.isAuthenticated());
}
@Override protected UserDetails retrieveUser(String username,UsernamePasswordAuthenticationToken authentication)
throws AuthenticationException
{
System.out.println("Method invoked : retrieveUser");
//I have Username,password:
//HOW CAN I ACCESS "myCustom1", "myCustom2" here ?
}
}
Решение
Если вам нужно использовать дополнительные параметры формы для манипулирования именем пользователя и паролем, вы можете реализовать свой собственный AuthenticationProcessingFilter
Этот класс будет иметь полный доступ к HttpRequest и, следовательно, ко всем дополнительным параметрам, которые вы отправляете. Если ваша цель состоит в том, чтобы как-то использовать эти значения для изменения имени пользователя и пароля, это то место, где вы должны это сделать. Р>
Другие советы
Все вышеперечисленное - это отличные и совершенные решения. Но я использовал обходное решение, которое прекрасно работает. Используется мультитенантный идентификатор для ThreadLocal
package com.mypackage.servlet;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.springframework.util.Assert;
public class ThreadLocalContextUtil implements Filter{
private static final ThreadLocal<Object> contextHolder =
new ThreadLocal<Object>();
public static void setTenantId(Object tenantId) {
Assert.notNull(tenantId, "customerType cannot be null");
contextHolder.set(tenantId);
}
public static Object getTenantId() {
return contextHolder.get();
}
public static void clearTenant() {
contextHolder.remove();
}
public void destroy() {
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
// Set the tenant Id into a ThreadLocal object
ThreadLocalContextUtil.setTenantId(request);
if(chain != null)
chain.doFilter(request, response);
else {
//error
}
}
public void init(FilterConfig filterconfig) throws ServletException {
}
}
xml весенней безопасности Р>
<security:http auto-config="true" use-expressions="true" access-denied-page="/forms/auth/403" >
<security:custom-filter before="FIRST" ref="tenantFilter" />
......
</security:http>
Доступ к объекту запроса в вашем классе аутентификации
HttpServletRequest currRequest = (HttpServletRequest) ThreadLocalContextUtil.getTenantId();
Затем используйте объект запроса для получения ваших пользовательских параметров
Хитрость здесь в том, что вам нужно создать новый AuthenicationToken (возможно), расширяющий UsernameAndPasswordAuthenicationToken, и, как говорит @emills, вам нужно затем реализовать новый AuthenciationProcessingFilter, чтобы сопоставить значения запроса с токеном и отправить их в AuthenicationManager.
По сути, реализация собственной цепочки аутентификации в Spring-Security состоит из нескольких частей.
AuthenicationToken
- сведения о запросе аутентификации и его результате, т. е. содержат учетные данные, необходимые для аутентификации.AuthenicationProvider
- зарегистрирован в AuthenicationManager, принимает ваш AuthenicationToken, проверяет пользователя и возвращает токен с установленными предоставленными полномочиями.AuthenciationFilter
- на самом деле это не обязательно должен быть фильтр, просто использование AbstractProcessingFilter сделает вашу жизнь немного проще.
Я бы пошел по этому пути:
<bean id="authenticationProcessingFilter"
class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilter">
...
<property name="authenticationDetailsSource">
<bean class="org.acegisecurity.ui.AuthenticationDetailsSourceImpl">
<property name="clazz"
value="com.MyAuthenticationDetails"/>
</bean>
</property>
</bean>
Это класс, который содержит свойства:
package com;
import javax.servlet.http.HttpServletRequest;
import org.acegisecurity.ui.WebAuthenticationDetails;
public class MyAuthenticationDetails extends WebAuthenticationDetails {
public MyAuthenticationDetails() {
super();
}
//This constructor will be invoqued by the filter
public MyAuthenticationDetails(HttpServletRequest request) {
super(request);
this.myCustom1 = request.getParameter("myCustom1");
}
public String getMyCustom1() {
return myCustom1;
}
private String myCustom1;
}
Теперь у вас есть имя пользователя, пароль и данные. Р>
Я сделал похожую вещь, но отличается от того, что здесь предлагали. Я не говорю, что это "правильно" способ сделать это - но это сработало очень хорошо для меня. В объекте Principal есть пользователь, а также объект Details в AuthenticationToken, в котором можно хранить карту (String, String) другой информации для входа в систему.
public class RequestFormDeatils extends SpringSecurityFilter {
protected void doFilterHttp(HttpServletRequest request, ...) {
SecurityContext sec = SecurityContextHolder.getContent();
AbstractAuthenticationToken auth = (AbstractAuthenticationToken)sec.getAuthentication();
Map<String, String> m = new HashMap<String, String>;
m.put("myCustom1", request.getParamtere("myCustom1"));
m.put("myCustom2", request.getParameter("myCustom2"));
auth.setDetails(m);
}
Теперь в любом месте вашего кода вы можете использовать SecurityContext для распространения этой информации, связанной с безопасностью, без необходимости связывать ее с вашим объектом UserDetails или передавать в качестве аргументов. Я делаю этот код в SecurityFilter в конце цепочки Spring Security Filter.
<bean id="requestFormFilter" class="...RequestFormDetails">
<custom-filter position="LAST" />
</bean>