Spring Security:UserDetailsS​​ervice仅运行一次

时间:2017-02-15 15:08:50

标签: spring spring-mvc spring-boot spring-security

我似乎错过了一些基本的东西:

@SpringBootApplication
public class Application {
    User u = new User("USER", "PASSWORD",AuthorityUtils.createAuthorityList(
                "ROLE_USER", "ROLE_ADMINISTRATOR"));
    @Bean
    public UserDetailsService userDetailsService() {
        // returning a new User object works fine for every request
        return username -> new User("USER", "PASSWORD",
            AuthorityUtils.createAuthorityList(
                    "ROLE_USER", "ROLE_ADMINISTRATOR"));
        // returning a previously created User object
        // works only for the first request,
        // subsequent requests get a 401 error
        // return username -> u; 
    }
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

使用spring-boot-starter-security依赖项的Spring Boot(v1.5.1)应用程序现在只知道一个用户。此外,只有这个用户才能访问它的所有端点。在我看到的所有工作示例中,UserDetailsService始终返回类型为User的新对象,就像上面的示例一样。

但是当它返回先前创建的对象(如上面名为u的对象)时,只有第一个请求被验证。为什么?

2 个答案:

答案 0 :(得分:0)

可以找到一个完整的例子,也可以找到JPA here

这只是一个例子。密码仍然需要加密/保护。

Application.java

package demo;

import java.util.Date;
import java.util.List;
import java.util.Map;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.data.repository.CrudRepository;
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.authentication.configurers.GlobalAuthenticationConfigurerAdapter;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

@Configuration
@ComponentScan
@EnableAutoConfiguration
@EnableGlobalMethodSecurity(securedEnabled = true)
public class Application extends WebMvcConfigurerAdapter {

    @Controller
    protected static class HomeController {

        @RequestMapping("/")
        @Secured("ROLE_ADMIN")
        public String home(Map<String, Object> model) {
            model.put("message", "Hello World");
            model.put("title", "Hello Home");
            model.put("date", new Date());
            return "home";
        }

    }

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/login").setViewName("login");
        registry.addViewController("/access").setViewName("access");
    }

    @Bean
    public ApplicationSecurity applicationSecurity() {
        return new ApplicationSecurity();
    }

    @Order(Ordered.HIGHEST_PRECEDENCE)
    @Configuration
    protected static class AuthenticationSecurity extends
            GlobalAuthenticationConfigurerAdapter {

        @Autowired
        private Users users;

        @Override
        public void init(AuthenticationManagerBuilder auth) throws Exception {
            auth.userDetailsService(users);
        }
    }

    @Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
    protected static class ApplicationSecurity extends WebSecurityConfigurerAdapter {

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            // @formatter:off
            http.authorizeRequests().antMatchers("/login").permitAll().anyRequest()
                    .fullyAuthenticated().and().formLogin().loginPage("/login")
                    .failureUrl("/login?error").and().logout()
                    .logoutRequestMatcher(new AntPathRequestMatcher("/logout")).and()
                    .exceptionHandling().accessDeniedPage("/access?error");
            // @formatter:on
        }

    }

}

@Service
class Users implements UserDetailsService {

    private UserRepository repo;

    @Autowired
    public Users(UserRepository repo) {
        this.repo = repo;
    }

    @Override
    public UserDetails loadUserByUsername(String username)
            throws UsernameNotFoundException {
        User user = repo.findByName(username);
        if (user == null) {
            return null;
        }
        List<GrantedAuthority> auth = AuthorityUtils
                .commaSeparatedStringToAuthorityList("ROLE_USER");
        if (username.equals("admin")) {
            auth = AuthorityUtils
                    .commaSeparatedStringToAuthorityList("ROLE_ADMIN");
        }
        String password = user.getPassword();
        return new org.springframework.security.core.userdetails.User(username, password,
                auth);
    }

}

@Repository
interface UserRepository extends CrudRepository<User, Long> {
    User findByName(String name);
}

@Entity
class User {
    @GeneratedValue
    @Id
    private Long id;
    private String name;
    private String password;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPassword() {
        return password;
    }

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

答案 1 :(得分:0)

我认为这是由于Spring User对象在身份验证后清除了密码

来自-> https://docs.spring.io/spring-security/site/docs/current/api/org/springframework/security/core/userdetails/User.html

  

请注意,此实现不是一成不变的。它实现了CredentialsContainer接口,以便允许在身份验证后删除密码。如果您将实例存储在内存中并重新使用它们,则可能会产生副作用。如果是这样,请确保每次调用UserDetailsService时都返回一个副本。