简体   繁体   中英

How to put User object into UsernamePasswordAuthenticationToken(AuthenticationProvider)?

I have faced with some problem when I try to put current User object into the UsernamePasswordAuthenticationToken returned by my CustomAuthenticationProvider. So at the beginning I need to show my spring-security.xml, it looks like this:

    <http auto-config="true" use-expressions="true">
    <intercept-url pattern="/sign/in" access="isAnonymous()" /> 
    <intercept-url pattern="/sign/up" access="isAnonymous()" /> 
    <intercept-url pattern="/secret/page" access="isAuthenticated()" />
    <intercept-url pattern="/sign/out" access="isAuthenticated()" />
    <intercept-url pattern="/user/myinfo" access="isAuthenticated()" />

    <form-login
        login-page="/sign/in"
        default-target-url="/secret/page"
        authentication-failure-url="/sign/in?failed=1"
        password-parameter="password"
        username-parameter="email"
    />
    <csrf disabled="true"/>
    <logout
        logout-url="/sign/out"
    />

    </http>
    <authentication-manager erase-credentials="false">
    <authentication-provider ref="customAuth">
    </authentication-provider>
    </authentication-manager>

    <beans:bean id="customAuth" class="milkiv.easyword.controller.sign.CustomAuthenticationProvider"/>

I have the page /sign/in where I enter my email and password to sign in. So with the next code of AuthenticationProvider I have always been redirected to the page which I defined as login-page="/sign/in"(not failure-url, I've checked). By the way, if put instead of User object just Username it works fine. So the code of mine AuthenticationProvider is the next:

@Service(value = "customAuth")
public class CustomAuthenticationProvider implements AuthenticationProvider {

    @Autowired
    public Storages storage;

    @Override
    @Transactional
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
    String login = authentication.getName();
    String password = authentication.getCredentials().toString();
    User user = storage.uSM.findByEmailAndPassword(login, password);
    if (user == null) {
        return null;
    } else {
        // if to pur here login insted of user it works fine
        return new UsernamePasswordAuthenticationToken(user, password);
    }
    }

    @Override
    public boolean supports(Class<?> authentication) {
    return authentication.equals(UsernamePasswordAuthenticationToken.class);
    }

}

Interesting is, that in my test, that looks:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:resources/spring-context.xml", "classpath:resources/spring-security.xml"})
@WebAppConfiguration
public class InTest {

    MockMvc mockMvc;

    Storages storages;

    @Autowired
    private Filter springSecurityFilterChain;

    private final String nickname = "LoremIpsum";
    private final String email = "lorem@ipsum.com";
    private final String password = "loreamipsumpassword";

    @Autowired
    WebApplicationContext wac; // cached

    @Before
    public void doBeforeTests() {
    mockMvc = MockMvcBuilders
        .webAppContextSetup(wac)
        .addFilters(springSecurityFilterChain)
        .build();
    ApplicationContext context = new ClassPathXmlApplicationContext("resources/spring-context.xml", "resources/spring-security.xml");
    storages = context.getBean(Storages.class);
    }

    @Test
    public void testSignIn() throws Exception {
    mockMvc.perform(
        formLogin()
            .user("email", email)
            .password(password)
    )
        .andExpect(redirectedUrl("/secret/page"));

} }

.andExpectFunction(redirectedUrl("/secret/page")) say that I have been redirected to the default-target-url="/secret/page", so looks like something goes wrong after that. As I have read AuthenticationProvider worked somehow with AuthenticatioManager, maybe the problem is somewhere here? Can someone explain me my mistake, or help to figure out what happen, or give some link which helps me to understand, or something else. I will appreciate any help. Thanks everyone in advance.

ADD User model:

public class User {
    private int userId;

    @NotBlank (message = "Email field can not be empty or missed")
    @Size(min = 5, max = 128, message = "Email field must have from 5 to 128 symbols")
    @Email(message = "Email field must have email format.")
    private String email;

    @NotBlank (message = "Password field can not be empty or missed")
    @Size (min = 5, max = 32, message = "Password field must have from 5 to 32 symbols")
    private String password;

    @NotBlank (message = "Confirm password field can not be empty or missed")
    @Size (min = 5, max = 32, message = "Confirm password field must have from 5 to 32 symbols")
    private String confirmPassword;

    @NotBlank (message = "Nickname field can not be empty or missed")
    @Size (min = 3, max = 32, message = "Nickname field must have from 3 to 32 symbols")
    private String nickname; 


    private Date registrationDate;

    private Set studyLanguages;

    public User(){
    }

    public int getUserId() {
    return userId;
    }

    public void setUserId(int userId) {
    this.userId = userId;
    }

    public String getEmail() {
    return email;
    }

    public void setEmail(String email) {
    this.email = email;
    }

    public String getPassword() {
    return password;
    }

    public void setPassword(String password) {
    this.password = password;
    }

    public String getNickname() {
    return nickname;
    }

    public void setNickname(String nickname) {
    this.nickname = nickname;
    }

    public Date getRegistrationDate() {
    return registrationDate;
    }

    public void setRegistrationDate(Date registration_date) {
    this.registrationDate = registration_date;
    }

    public Set getStudyLanguages() {
    return studyLanguages;
    }

    public void setStudyLanguages(Set studyLanguages) {
    this.studyLanguages = studyLanguages;
    }


    public String getConfirmPassword() {
    return confirmPassword;
    }

    public void setConfirmPassword(String confirmPassword) {
    this.confirmPassword = confirmPassword;
    }
}

I think the problem is you are using the UsernamePasswordAuthenticationToken(user, password) the wrong way. Here the method UsernamePasswordAuthenticationToken wait for two arguments : a principal ( usually a string username) and a password.

You are doing great by getting them from the Authentification object but the user object isn't considered as a principal, the login is.

    @Service(value = "customAuth")
public class CustomAuthenticationProvider implements AuthenticationProvider {

    @Autowired
    public Storages storage;

    @Override
    @Transactional
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
    String login = authentication.getName();
    String password = authentication.getCredentials().toString();
    User user = storage.uSM.findByEmailAndPassword(login, password);
    if (user == null) {
        return null;
    } else {
        // Here use the user object to only check if the user exists in the database if not null use his login ( principal ) and password
        return new UsernamePasswordAuthenticationToken(login, password);
    }
    }

    @Override
    public boolean supports(Class<?> authentication) {
    return authentication.equals(UsernamePasswordAuthenticationToken.class);
    }
}

SOME ADDITIONAL RESOURCES :

Here is a link where you can do the same thing with your user object this time. You will be using UserDetails and UserDetails

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM