简体   繁体   English

管理员请求的春季安全配置错误

[英]Spring security configuration error for admin request

I'm trying to configure my Spring application to have a pretty basic security system with Spring security. 我正在尝试将我的Spring应用程序配置为具有带有Spring安全性的相当基本的安全性系统。 I would like to have resources to be served without security filters, standard pages to be filtered checking if the user has the role "User" and /admin/ pages to be served checking if the role is "Admin". 我希望没有安全过滤器即可提供资源,要过滤标准页面以检查用户是否具有角色“ User”,而要提供/ admin /页面以检查角色是否为“ Admin”。

What I have is: 我所拥有的是:

springSecurityFilterChain org.springframework.web.filter.DelegatingFilterProxy springSecurityFilterChain org.springframework.web.filter.DelegatingFilterProxy

<filter-mapping>
    <filter-name>springSecurityFilterChain</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

in the web.xml, while the spring-security.xml is: 在web.xml中,而spring-security.xml是:

<security:http security="none" pattern="/resources/**"/>
<security:http auto-config="true" use-expressions="true" >
    <security:intercept-url pattern="/admin/**" access="hasRole('Admin')" />
    <security:logout logout-success-url="/welcome" logout-url="/logout" />
    <security:form-login login-page="/FormLogin"
        default-target-url="/welcome"
        username-parameter="username"
        password-parameter="hashPwd" 
        authentication-failure-url="/login?error"
        />
</security:http>

<security:authentication-manager>
   <security:authentication-provider user-service-ref="controlloUtente">
     <security:password-encoder hash="bcrypt" />  
  </security:authentication-provider>
</security:authentication-manager>


<beans:bean id="controlloUtente"
    class="org.fabrizio.fantacalcio.utility.SpringSecurityServiceImpl">
</beans:bean>

Then I configured this class: 然后,我配置了此类:

package org.fabrizio.fantacalcio.utility;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.fabrizio.fantacalcio.model.beans.Utente;
import org.fabrizio.fantacalcio.model.dao.UtenteDaoImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@Transactional
public class SpringSecurityServiceImpl implements UserDetailsService{

    @Autowired
    private UtenteDaoImpl utenteDao;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        Utente utente = utenteDao.getUtenteByUsername(username);
        if(utente == null){
            throw new UsernameNotFoundException("L'utente inserito non è stato trovato");
        }
        return convertUtente(utente);
    }

    private UserDetails convertUtente(Utente utente) {
        UserDetails ud = new User(utente.getUsername(), utente.getHashPwd(), true, true, true, true, getRoles(utente));
        return ud;
    }

    private Collection<? extends GrantedAuthority> getRoles(Utente utente) {
        GrantedAuthority auth = new SimpleGrantedAuthority(utente.getRuolo().getNome());
        List<GrantedAuthority> listaAuth = new ArrayList<GrantedAuthority>();
        listaAuth.add(auth);
        return listaAuth;
    }

}

and the following one: 和以下之一:

package org.fabrizio.fantacalcio.utility;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.core.PriorityOrdered;
import org.springframework.security.access.annotation.Jsr250MethodSecurityMetadataSource;
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
import org.springframework.stereotype.Component;

@Component
public class DefaultRolesPrefixPostProcessor implements BeanPostProcessor, PriorityOrdered {

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName)
            throws BeansException {
        if(bean instanceof Jsr250MethodSecurityMetadataSource) {
            ((Jsr250MethodSecurityMetadataSource) bean).setDefaultRolePrefix("");
        }
        if(bean instanceof DefaultMethodSecurityExpressionHandler) {
            ((DefaultMethodSecurityExpressionHandler) bean).setDefaultRolePrefix("");
        }
//        if(bean instanceof DefaultWebSecurityExpressionHandler) {
//            ((DefaultWebSecurityExpressionHandler) bean).setDefaultRolePrefix("");
//        }
        return bean;
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName)
            throws BeansException {
        return bean;
    }

    @Override
    public int getOrder() {
        return PriorityOrdered.HIGHEST_PRECEDENCE;
    }
}

But nothing works. 但是什么都行不通。 My formLogin doesn't use spring forms but a classic form. 我的formLogin不使用弹簧形式,而是经典形式。

When I'm logged in and I try to get a pge like /admin/testpage , I get redirected to FormLogin page, even if I have admin role, and this is the debug output: 登录后,尝试获取/admin/testpage pge,即使我具有管理员角色,我也被重定向到FormLogin页面,这是调试输出:

22/06/2015 10:03:04 - DEBUG - (AntPathRequestMatcher.java:141) - Request '/admin/formregistrazione' matched by universal pattern '/**'
22/06/2015 10:03:04 - DEBUG - (HttpSessionRequestCache.java:43) - DefaultSavedRequest added to Session: DefaultSavedRequest[http://localhost:8080/Fantacalcio/admin/FormRegistrazione]
22/06/2015 10:03:04 - DEBUG - (ExceptionTranslationFilter.java:202) - Calling Authentication entry point.
22/06/2015 10:03:04 - DEBUG - (DefaultRedirectStrategy.java:39) - Redirecting to 'http://localhost:8080/Fantacalcio/FormLogin'
22/06/2015 10:03:04 - DEBUG - (HttpSessionSecurityContextRepository.java:337) - SecurityContext is empty or contents are anonymous - context will not be stored in HttpSession.

Sometimes, after login, I get this message: 有时,登录后,我收到以下消息:

HTTP Status 403 - Invalid CSRF Token 'null' was found on the request parameter '_csrf' or header 'X-CSRF-TOKEN'.

What did I do wrong? 我做错了什么? I ALWAYS have to use the token? 我总是必须使用令牌吗? And why the flow never goes into the SpringSecurityServiceImpl class? 为什么流程永远不会进入SpringSecurityServiceImpl类?

Thanks 谢谢

EDIT: I disabled csrf and it's getting clear now. 编辑:我禁用了csrf ,现在越来越清楚了。 The problem is in my SpringSecurityServiceImpl the Autowired utenteDao instance is null. 这个问题在我SpringSecurityServiceImplAutowired utenteDao实例为null。 I edited the spring-security.xml file like this, hoping Spring understands the @Service annotated class but it doesn't work: 我像这样编辑spring-security.xml文件,希望Spring能够理解@Service注释类,但是它不起作用:

<security:authentication-manager>
       <security:authentication-provider user-service-ref="SpringSecurityServiceImpl">
         <security:password-encoder hash="bcrypt" />  
      </security:authentication-provider>
    </security:authentication-manager>

My UtenteDao class 我的UtenteDao课程

package org.fabrizio.fantacalcio.model.dao;

import java.util.List;

import org.fabrizio.fantacalcio.model.beans.Utente;
import org.hibernate.criterion.Restrictions;
import org.springframework.stereotype.Repository;

@Repository
public class UtenteDaoImpl extends BaseDaoImpl<Utente> implements UtenteDao{

     public UtenteDaoImpl() {
    System.out.println("test");
    }

    @SuppressWarnings("unchecked")
    public List<Utente> trovaUtentiAttivi(){
        return getSession().createCriteria(Utente.class).add(Restrictions.eq("attivo", true)).list();
    }


    @SuppressWarnings("unchecked")
    public Utente getUtenteFromCredenziali(Utente utente){
        List<Utente> utenteTrovato = getSession().createCriteria(Utente.class)
                .add(Restrictions.eq("username", utente.getUsername()))
                .add(Restrictions.eq("hashPwd", utente.getHashPwd()))
                .list();
        Utente utenteLoggato = utenteTrovato.size() == 0 ? null : utenteTrovato.get(0);
        return utenteLoggato;
    }

    public Boolean usernameExists(String username){
        return getSession().createCriteria(Utente.class)
                .add(Restrictions.eq("username", username))
                .list().size() > 0;
    }

    @SuppressWarnings("unchecked")
    public Utente getUtenteByUsername(String username){
        List<Utente> utenteTrovato = getSession().createCriteria(Utente.class)
                .add(Restrictions.eq("username", username))
                .list();
        Utente utenteLoggato = utenteTrovato.size() == 0 ? null : utenteTrovato.get(0);
        return utenteLoggato;
    }
}

Spring security login and logout forms expect CSRF tokens to be sent in the post request. Spring安全性登录和注销表单期望CSRF令牌在发布请求中发送。 You can add the token using hidden variable: 您可以使用隐藏变量添加令牌:

 <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>

Add above line in spring security login form. 在Spring Security登录表单中添加以上行。 And logout is also need to be a post request method. 和退出也需要一个post请求方法。 Every post request to server is gonna check for these tokens which you can set into request header. 对服务器的每个发布请求都将检查这些令牌,您可以将其设置为请求标头。 If you don't want to do this and disable csrf token security. 如果您不想这样做,请禁用csrf令牌安全性。 You can disable it using csrf tag in http section of security config. 您可以使用安全配置的http部分中的csrf标记禁用它。 csrf has attribute to disable it. csrf具有禁用它的属性。

<http>
    <csrf />
</http>

Read this article to know more. 阅读本文以了解更多信息。

EDIT: 编辑:

You have mentioned user-service-ref="SpringSecurityServiceImpl" in security authentication tag but Service class does not have an identifier. 您已经在安全认证标签中提到了user-service-ref="SpringSecurityServiceImpl" ,但是Service类没有标识符。 Give your service class an identifier, prefer camel case characters. 给您的服务类别一个标识符,最好使用驼峰字符。

@Service("springSecurityServiceImpl") // talking about this identifier
@Transactional
public class SpringSecurityServiceImpl implements UserDetailsService {}

And authentication provider bean should be: 身份验证提供程序bean应该是:

<security:authentication-manager>
   <security:authentication-provider user-service-ref="springSecurityServiceImpl">
     <security:password-encoder hash="bcrypt" />  
  </security:authentication-provider>
</security:authentication-manager>

And also check if your DAO class is marked as @Repository component for autowire to work perfectly. 还要检查您的DAO类是否标记为@Repository组件,以使自动装配完美工作。

I solved the problem. 我解决了问题。 Searching around I found out that security and servlet have different context, so basically I had to add the <context:component-scan base-package="org.fabrizio.fantacalcio.utility, org.fabrizio.fantacalcio.model.dao" /> on the security-config.xml file too, declaring only the package to scan to make the security work, as it doesn't share the beans declared in the dispatcher config file. 到处搜索,我发现安全性和servlet具有不同的上下文,因此基本上我必须添加<context:component-scan base-package="org.fabrizio.fantacalcio.utility, org.fabrizio.fantacalcio.model.dao" />也在security-config.xml文件中,仅声明要扫描以使安全性起作用的包,因为它不共享在分派器配置文件中声明的bean。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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