Je suis perplexe avec SpringSecurity. Il existe de nombreuses façons d'implémenter une chose simple et je les ai toutes mélangées.
Mon code est le suivant mais il lève une exception. Si je supprime UserDetailsService
les codes associés, l'application s'exécute et je peux me connecter in-memory
utilisateurs. Comme suggéré ci-dessous, j'ai converti la configuration en XML, mais les utilisateurs ne peuvent pas se connecter.
org.springframework.beans.factory.BeanCreationException: Error creating bean
with name 'securityConfig': Injection of autowired dependencies failed; nested
exception is org.springframework.beans.factory.BeanCreationException: Could
not autowire field:
org.springframework.security.core.userdetails.UserDetailsService
com.myproj.config.SecurityConfig.userDetailsService; nested exception is
org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying
bean of type
[org.springframework.security.core.userdetails.UserDetailsService] found for
dependency: expected at least 1 bean which qualifies as autowire candidate for
this dependency. Dependency annotations:
{@org.springframework.beans.factory.annotation.Autowired(required=true),
@org.springframework.beans.factory.annotation.Qualifier(value=userDetailsService)}
Caused by: org.springframework.beans.factory.BeanCreationException: Could not
autowire field
org.springframework.security.core.userdetails.UserDetailsService
com.myproj.config.SecurityConfig.userDetailsService; nested exception is
org.springframework.beans.factory.NoSuchBeanDefinitionException:
No qualifying bean of type
[org.springframework.security.core.userdetails.UserDetailsService]
found for dependency: expected at least 1 bean which qualifies as autowire
candidate for this dependency. Dependency annotations:
{@org.springframework.beans.factory.annotation.Autowired(required=true),
@org.springframework.beans.factory.annotation.Qualifier(value=userDetailsService)}
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException:
No qualifying bean of type
[org.springframework.security.core.userdetails.UserDetailsService] found for
dependency: expected at least 1 bean which qualifies as autowire candidate for
this dependency. Dependency annotations:
{@org.springframework.beans.factory.annotation.Autowired(required=true),
@org.springframework.beans.factory.annotation.Qualifier(value=userDetailsService)}
Web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://Java.Sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://Java.Sun.com/xml/ns/javaee
http://Java.Sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<listener>
<listener-class>org.Apache.tiles.extras.complete.CompleteAutoloadTilesListener</listener-class>
</listener>
<servlet>
<servlet-name>proj</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>proj</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
MvcWebApplicationInitializer
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
public class MvcWebApplicationInitializer
extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] { SecurityConfig.class };
}
@Override
protected Class<?>[] getServletConfigClasses() {
return null;
}
@Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
}
SecurityWebApplicationInitializer
import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
public class SecurityWebApplicationInitializer
extends AbstractSecurityWebApplicationInitializer {
}
SecurityConfig
@Configuration
@EnableWebMvcSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
@Qualifier("userDetailsService")
UserDetailsService userDetailsService;
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth)
throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(
passwordEncoder());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/resources/**", "/", "/index", "/aboutus")
.permitAll()
.antMatchers("/profile/**")
.hasRole("USER")
.and()
.formLogin().loginPage("/signin").failureUrl("/signin?error")
.permitAll().and().logout().logoutUrl("/signout").permitAll();
}
@Bean
public PasswordEncoder passwordEncoder() {
PasswordEncoder encoder = new BCryptPasswordEncoder();
return encoder;
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception
{
return super.authenticationManagerBean();
}
}
MemberServiceImpl
@Service("userDetailsService")
public class MemberServiceImpl implements UserDetailsService {
@Autowired
MemberRepository memberRepository;
private List<GrantedAuthority> buildUserAuthority(String role) {
Set<GrantedAuthority> setAuths = new HashSet<GrantedAuthority>();
setAuths.add(new SimpleGrantedAuthority(role));
List<GrantedAuthority> result = new ArrayList<GrantedAuthority>(
setAuths);
return result;
}
private User buildUserForAuthentication(Member member,
List<GrantedAuthority> authorities) {
return new User(member.getEmail(), member.getPassword(),
member.isEnabled(), true, true, true, authorities);
}
@Override
@Transactional(readOnly = true)
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
Member member = memberRepository.findByUserName(username);
List<GrantedAuthority> authorities = buildUserAuthority("Role");
return buildUserForAuthentication(member, authorities);
}
}
mise à jour 1
Même après avoir ajouté l'annotation suivante et la méthode authenticationManagerBean
de SecurityConfig, la même exception est levée.
@EnableGlobalMethodSecurity(prePostEnabled = true)
mise à jour 2
Comme suggéré dans l'une des réponses, je l'ai converti en configuration basée sur XML, le code actuel est le suivant; cependant, lorsque je soumets un formulaire de connexion, il ne fait rien.
Spring-Security.xml
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.0.xsd">
<beans:import resource='login-service.xml' />
<http auto-config="true" access-denied-page="/notFound.jsp"
use-expressions="true">
<intercept-url pattern="/" access="permitAll" />
<form-login login-page="/signin" authentication-failure-url="/signin?error=1"
default-target-url="/index" />
<remember-me />
<logout logout-success-url="/index.jsp" />
</http>
<authentication-manager>
<authentication-provider>
<!-- <user-service> <user name="admin" password="secret" authorities="ROLE_ADMIN"/>
<user name="user" password="secret" authorities="ROLE_USER"/> </user-service> -->
<jdbc-user-service data-source-ref="dataSource"
users-by-username-query="
select username,password,enabled
from Member where username=?"
authorities-by-username-query="
select username
from Member where username = ?" />
</authentication-provider>
</authentication-manager>
</beans:beans>
login-service.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost/testProject" />
<property name="username" value="root" />
<property name="password" value="" />
</bean>
</beans>
Je pense que vous oubliez d'ajouter cette annotation sur la classe SecurityConfig
@Configuration
@EnableWebMvcSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
@Qualifier("userDetailsService")
UserDetailsService userDetailsService;
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth)
throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(
passwordEncoder());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/resources/**", "/", "/index", "/aboutus")
.permitAll().antMatchers("/profile/**").hasRole("USER").and()
.formLogin().loginPage("/signin").failureUrl("/signin?error")
.permitAll().and().logout().logoutUrl("/signout").permitAll();
}
@Bean
public PasswordEncoder passwordEncoder() {
PasswordEncoder encoder = new BCryptPasswordEncoder();
return encoder;
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
et une chose de plus je pense que ce haricot n'est pas nécessaire
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
Veuillez essayer cet espoir que cela fonctionnera pour vous ..
Pour obtenir l'utilisateur actuel
public String getUsername() {
SecurityContext context = SecurityContextHolder.getContext();
Authentication authentication = context.getAuthentication();
if (authentication == null)
return null;
Object principal = authentication.getPrincipal();
if (principal instanceof UserDetails) {
return ((UserDetails) principal).getUsername();
} else {
return principal.toString();
}
}
public User getCurrentUser() {
if (overridenCurrentUser != null) {
return overridenCurrentUser;
}
User user = userRepository.findByUsername(getUsername());
if (user == null)
return user;
}
Merci
Je pense que le problème pourrait être dû à l'absence de @ComponentScan
annotation. Lors d'une tentative de câblage automatique userDetailsService
dans SecurityConfig
, il n'est pas en mesure de trouver un bean approprié pour le câblage automatique.
Une application Spring a généralement un "contexte d'application" distinct, en plus du "contexte mvc", du "contexte de sécurité" (que vous avez déjà via SecurityConfig
), etc.
Je ne sais pas si mettre @ComponentScan
on SecurityConfig
lui-même ne fonctionnera pas, mais vous pouvez l'essayer:
@Configuration
@ComponentScan("your_base_package_name_here")
@EnableWebMvcSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
...
}
Remplacez "your_base_package_name_here" par le nom du package contenant votre @Component
ou @Service
Des classes.
Si cela ne fonctionne pas, ajoutez une nouvelle classe vide avec @ComponentScan
annotation:
@Configuration
@ComponentScan("your_base_package_name_here")
public class AppConfig {
// Blank
}
Source: http://docs.spring.io/spring-javaconfig/docs/1.0.0.M4/reference/html/ch06s02.html
Voir qu'il y a des erreurs dans votre base de code, essayez de le résoudre en voyant le code ci-dessous.
Supprimez votre fichier SecurityConfig et convertissez-le en une configuration basée sur un fichier xml.
Votre spring-security.xml devrait ressembler à ceci.
<security:http auto-config="true" >
<security:intercept-url pattern="/index*" access="ROLE_USER" />
<security:form-login login-page="/login" default-target-url="/index"
authentication-failure-url="/fail2login" />
<security:logout logout-success-url="/logout" />
</security:http>
<security:authentication-manager>
<security:authentication-provider>
<!-- <security:user-service>
<security:user name="samplename" password="sweety" authorities="ROLE_USER" />
</security:user-service> -->
<security:jdbc-user-service data-source-ref="dataSource"
users-by-username-query="select username, password, active from users where username=?"
authorities-by-username-query="select us.username, ur.authority from users us, user_roles ur
where us.user_id = ur.user_id and us.username =? "
/>
</security:authentication-provider>
</security:authentication-manager>
web.xml devrait ressembler à ceci:
<servlet>
<servlet-name>sdnext</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>sdnext</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/sdnext-*.xml,
</param-value>
</context-param>
<welcome-file-list>
<welcome-file>index</welcome-file>
</welcome-file-list>
<!-- Spring Security -->
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>
org.springframework.web.filter.DelegatingFilterProxy
</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Essayez d'ajouter la méthode suivante à votre SecurityConfig:
@Bean
public UserDetailsService userDetailsServiceBean() throws Exception {
return super.userDetailsServiceBean();
}
Spring ne peut pas trouver le bean avec le qualificatif userDetailsService
. Je pense que vous devriez vérifier votre fichier applicationContext.xml
Au cas où vous auriez oublié de configurer le bean de UserDetailsService
pour Spring Security. S'il y est, essayez une fois en supprimant @Qualifier("userDetailsService")
.
suivez ce lien. fichier context.xml configuré par rapport à la sécurité Spring
Il semble que votre bean "userDetailsService" est déclaré @ Autowired, mais il n'est pas disponible en tant que classe ( MemberServiceImpl ) dans le contexte de votre SecurityConfig .
Je suppose que dans votre MvcWebApplicationInitializer , vous devez inclure MemberServiceImpl comme:
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] { SecurityConfig.class, MemberServiceImpl.class };
}
Voici la réponse, en utilisant le @ComponentScan approprié, vous trouverez ci-dessous un exemple d'extrait de code que je colle, que j'ai également rencontré le même problème et résolu. Ci-dessous est résolu et fonctionne pour le problème lié à l'exception de création de bean pour org.springframework.security.core.userdetails.UserDetailsService
Step1: Écrivez la classe de configuration d'application de sécurité
import org.springframework.security.core.userdetails.UserDetailsService;
@Configuration
@EnableWebSecurity
public class LoginSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
@Qualifier("userDetailsServiceImpl")
UserDetailsService userDetailsService;
Ici, @ComponentScan n'est pas obligatoire dans LoginSecurityConfig , vous pouvez définir @ComponentScan dans la classe de configuration racine comme ci-dessous et importer le LoginSecurityConfig.class LoginSecurityConfig.
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = { "com.example" })
@Import(value = { LoginSecurityConfig.class })
public class LoginApplicationConfig
Step2: Maintenant, câblage automatique du SpringBean org.springframework.security.core.userdetails.UserDetailsService
@Service("userDetailsServiceImpl")
public class UserDetailsServiceImpl implements org.springframework.security.core.userdetails.UserDetailsService {
@Autowired
UserDao userDao;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userDao.findByUsername(username);
if (user == null) {
System.out.println("User not found");
throw new UsernameNotFoundException("Username not found");
}
return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), true, true, true, true, getGrantedAuthorities(user));
}
private List<GrantedAuthority> getGrantedAuthorities(User user) {
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
return authorities;
}
}//End of Class
Essayez de changer le type de champ:
@Configuration
@EnableWebMvcSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
@Qualifier("userDetailsService")
MemberServiceImpl userDetailsService;
J'utilise Wicket et j'ai rencontré le même problème. Je pourrais résoudre ce problème en modifiant l'ordre dans ma classe AppInit pour analyser le package en premier, puis enregistrer le bean appelant
public class AppInit implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext servletContext) throws ServletException
{
// Create webapp context
AnnotationConfigWebApplicationContext root = new AnnotationConfigWebApplicationContext();
root.scan("my_package");
root.register(SpringSecurityConfiguration.class);
...#
}