6

正如标题所说,我正在开发一个从外部应用程序接收用户身份验证信息的 Web 应用程序。我的应用程序的弹簧控制器获取用户信息并将其存储在会话中。我想在 Spring Security 中对这个用户进行身份验证,然后使用他的角色来授予/拒绝对 url 的访问,例如

<intercept-url pattern="/myprotectedpage*" access="hasRole('rightrole')" />

我阅读了一些关于 PRE_AUTH_FILTER 和 UserDetailsS​​ervice 的教程,但我不明白这一点。Spring Security 的应用生命周期是什么?涉及哪些类?我需要一些完整的工作样本。

4

3 回答 3

1

有很多相同的东西,只需要正确谷歌即可。

无论如何,迄今为止我发现的最好的(几乎所有的春季短靴)是克拉姆斯,这是基本春季安全的一个。

http://krams915.blogspot.com/2010/12/spring-security-mvc-integration_18.html

为了实现 UserDetailService 这里是链接

http://krams915.blogspot.in/2012/01/spring-security-31-implement_5023.html

其他一些是:

  1. 春天的例子
  2. MK 杨
  3. 还有 SpringSource 站点本身

编辑

这就是我自己的应用程序进行身份验证的方式(请注意,我不使用外部身份验证,我只是从数据库获取详细信息,但我想这应该不是什么大问题)。

我的security-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<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">

    <global-method-security pre-post-annotations="enabled" jsr250-annotations="enabled" secured-annotations="enabled">
    </global-method-security>

    <http use-expressions="true">
        <intercept-url pattern="/favicon.ico" access="permitAll" />
        <intercept-url pattern="/static/**" access="permitAll"/>
        <intercept-url pattern="/login.jsp*" access="permitAll"/> 
        <intercept-url pattern="/Admin/**" access="hasAnyRole('ROLE_SUPER_USER')"/>
        <intercept-url pattern="/**" access="hasAnyRole('ROLE_USER','ROLE_SUPER_USER','ROLE_ADMIN'"/>
        <form-login login-page="/login.jsp" authentication-failure-url="/login.jsp?login_error=1" />
        <http-basic/>
        <logout logout-success-url="/login.jsp"/>
        <remember-me user-service-ref="loginService" /
     </http>

    <authentication-manager>
        <authentication-provider user-service-ref="loginService">
         <password-encoder hash="md5"/>
        </authentication-provider>
    </authentication-manager>

    <beans:bean id="loginService" class="com.indyaah.service.LoginService">
    </beans:bean>
    <beans:bean id="authService" class="com.indyaah.service.AuthService" />
</beans:beans>

现在,正如您所见,我指定了一个名为loginService我的身份验证提供程序的 bean,它是 class 的 bean com.indyaah.service.LoginService

相同的代码是: 请注意我已经截断了不必要的代码

package com.indyaah.service;
..
@Service
public class LoginService implements UserDetailsService {

....

    /**
     * Implementation for custom spring security UserDetailsService
     */
    public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException, DataAccessException {
        logger.debug("Inside get member by username");
        if (userName != null) {
            Member memberVO = memberMapper.getMemberByUsername(userName);
            if (memberVO != null) {
                ArrayList<String> authList = memberRolesMapper.getMemberRoles(memberVO.getMemberId());

                List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
                for (String role : authList) {
                    System.out.println(role);
                    authorities.add(new GrantedAuthorityImpl(role.toString()));
                }

                if (memberVO.getEnabled()) {
                    User user = new User(memberVO.getUserName(), memberVO.getPassword(), true, true, true, true, authorities);
                    return user;
                } else {
                    logger.error("User with login: " + userName + " not Enabled in database. Authentication failed for user ");
                    throw new UsernameNotFoundException("User Not Enabled");
                }
            } else {
                logger.error("User with login: " + userName + " not found in database. Authentication failed for user ");
                throw new UsernameNotFoundException("user not found in database");
            }
        } else {
            logger.error("No User specified in the login ");
            throw new UsernameNotFoundException("No username specified");
        }

    }
}

注意这里的两件事。

  1. 我获取用户详细信息(在我的情况下,来自 DB,您的可能不同。)并将其放在一个新org.springframework.security.core.userdetails.User对象下,然后该方法将其返回给 spring security。
  2. 还有当局(我根据我的数据库架构从数据库中单独加载,你的情况可能会有所不同)并通过相同的用户对象将其传递给弹簧安全。
于 2012-09-11T09:58:05.477 回答
0

您可以实现自己的 Custom AuthenticationManager和 Custom UsernamePasswordAuthenticationFilter。这是一个简单的示例,但它也可以为您的信息提供一个想法,这是安全上下文中非常敏感的部分:)

只需在 spring_security.xml 中创建 bean:

<http entry-point-ref="authenticationProcessingFilterEntryPoint"
        use-expressions="true">

        <custom-filter ref="sessionManagementFilter" before="SESSION_MANAGEMENT_FILTER" />
        <custom-filter ref="customUsernamePasswordAuthenticationFilter"
            position="FORM_LOGIN_FILTER" />

        <session-management
            session-authentication-strategy-ref="sas"></session-management>
    </http>


    <beans:bean id="authenticationProcessingFilterEntryPoint"
            class="org.springframework.security.web.authentication.AuthenticationProcessingFilterEntryPoint">
            <beans:property name="loginFormUrl" value="/login" />
        </beans:bean>

        <beans:bean id="sas"
            class="org.springframework.security.web.authentication.session.SessionFixationProtectionStrategy" />

        <beans:bean id="customAuthenticationManager"
            class="my.package.security.CustomAuthenticationManager" />

        <beans:bean id="customUsernamePasswordAuthenticationFilter"
            class="my.package.security.CustomUsernamePasswordAuthenticationFilter">

            <beans:property name="sessionAuthenticationStrategy"
                ref="sas" />

            <beans:property name="authenticationManager" ref="customAuthenticationManager" />


            <beans:property name="authenticationSuccessHandler">

                <beans:bean
                    class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler">

                    <beans:property name="defaultTargetUrl" value="/main.xhtml" />

                </beans:bean>

            </beans:property>

            <beans:property name="authenticationFailureHandler">

                <beans:bean
                    class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">

                    <beans:property name="defaultFailureUrl" value="/login.xhtml" />

                </beans:bean>

            </beans:property>
        </beans:bean>
<beans:bean id="sessionManagementFilter"
        class="org.springframework.security.web.session.SessionManagementFilter">
        <beans:constructor-arg name="securityContextRepository"
            ref="httpSessionSecurityContextRepository" />
    </beans:bean>
    <beans:bean id="httpSessionSecurityContextRepository"
        class="org.springframework.security.web.context.HttpSessionSecurityContextRepository" />

当您实现 CustomUsernamePasswordAuthenticationFilter 覆盖 Authentication 并添加您的外部逻辑时:

public final class CustomUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter {

@Override
    public Authentication attemptAuthentication(HttpServletRequest request,   HttpServletResponse response){
CustomAuthentication auth = new CustomAuthentication();

            // set details of current user
            auth.setDetails(new WebAuthenticationDetails(request));
            auth.setAuthenticated(true);
            auth.setUserName(username);

            // set authentication to current security session
            LOGGER.info("Setting authentication into existing security context");
            SecurityContextHolder.getContext().setAuthentication(auth);

            // if validation done return generated authentication
            return auth;

}

}

然后生成的身份验证对象将由身份验证管理器处理:

public final class CustomAuthenticationManager implements AuthenticationManager {

    /*
     * (non-Javadoc)
     * 
     * @see org.springframework.security.authentication.AuthenticationManager#
     * authenticate(org.springframework.security.core.Authentication)
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(CustomUsernamePasswordAuthenticationFilter.class);
    private final BadCredentialsException badCredentialsException = new BadCredentialsException("Invalid username/password");
    @Override
    public Authentication authenticate(Authentication authentication) {

        //check if user has valid authentication
        if (authentication == null) {
            LOGGER.debug("Null authentication");
            throw badCredentialsException;
        }
        //Check mandatory fields
        if (!Validator.isValidString((String) authentication.getPrincipal()) || !Validator.isValidString((String) authentication.getCredentials())) {
            LOGGER.debug("Null/blank username/credential");
            throw badCredentialsException;

        }
        //Check if there is any role assigned into user
        if (authentication.getAuthorities() != null && authentication.getAuthorities().size() < 1) {
            LOGGER.debug("No authority found");
            throw badCredentialsException;
        }

        //Validate role
        //IF ROLE VALIDATION REQUIRED YOU CAN HANDLE IT HERE
        boolean authorityValid = false;
        LOGGER.info("Validating user authentication. Total grantedAuth size: " + authentication.getAuthorities().size());
        for (GrantedAuthority g : authentication.getAuthorities()) {
            if (!authorityValid) {
                //Testing purpose one type role available, when exact roles prepared create enum types
                authorityValid = g.getAuthority().equals("ROLE_LDAP_AUTHENTICATED");
            }
        }

        //if none of role matching to required throw exception
        if(!authorityValid){
            LOGGER.debug("User has authority but none of them matching");
            throw badCredentialsException;
        }

        LOGGER.info("Final validation done returning authentication");
        return authentication;
    }

}

然后,如果需要,您也可以覆盖默认身份验证对象,如果角色动态位于此处是您处理的位置:

public final class CustomAuthentication implements Authentication {

        /**
         * 
         */
        private static final long serialVersionUID = 1L;
        private transient String userName;
        private transient boolean authenticated;
        private transient Object details;
        private static final transient String ROLE = "ROLE_LDAP_AUTHENTICATED";

        /*
         * (non-Javadoc)
         * 
         * @see java.security.Principal#getName()
         */
        @Override
        public String getName() {
            return this.userName; //for dynamic username logic here
        }



     //IF ROLES DYNAMICALLY ALLOCATED ASSIGN IT HERE, HERE IS WHERE YOU READ FROM INTERCEPT URL

        /*
         * (non-Javadoc)
         * 
         * @see org.springframework.security.core.Authentication#getAuthorities()
         */
        @Override
        public Collection<GrantedAuthority> getAuthorities() {
            Collection<GrantedAuthority> auths = new ArrayList<GrantedAuthority>();
            auths.add(new GrantedAuthority() {

                /**
                 * 
                 */
                private static final long serialVersionUID = 1L;

                @Override
                public String getAuthority() {
                    if (authenticated) {

                        return ROLE;
                    }
                    return null;
                }
            });
            return auths;
        }

        /*
         * (non-Javadoc)
         * 
         * @see org.springframework.security.core.Authentication#getCredentials()
         */
        @Override
        public Object getCredentials() {
            //TODO: a specific algorithm can be stored
            return userName + " is ldap authenticated user";
        }

        /*
         * (non-Javadoc)
         * 
         * @see org.springframework.security.core.Authentication#getDetails()
         */
        @Override
        public Object getDetails() {
            return this.details;
        }

        /*
         * (non-Javadoc)
         * 
         * @see org.springframework.security.core.Authentication#getPrincipal()
         */
        @Override
        public Object getPrincipal() {
            return userName;
        }

        /*
         * (non-Javadoc)
         * 
         * @see org.springframework.security.core.Authentication#isAuthenticated()
         */
        @Override
        public boolean isAuthenticated() {

            return this.authenticated;
        }

        /*
         * (non-Javadoc)
         * 
         * @see
         * org.springframework.security.core.Authentication#setAuthenticated(boolean
         * )
         */
        @Override
        public void setAuthenticated(boolean arg0) {
            this.authenticated = arg0;

        }

        public String getUserName() {
            return userName;
        }

        public void setUserName(String userName) {
            this.userName = userName;
        }

        public void setDetails(Object details) {
            this.details = details;
        }

    }
于 2012-09-11T10:25:03.460 回答
0

实现一个保存用户信息的服务。

@Service
public class UserAuthenticationInfoService {

    private final Map<String, UserInfo> infos = new HashMap<String, UserInfo>();

    public void addUserInfo(UserInfo userInfo){
        infos.put(userInfo.getUsername(), userInfo);   
    }

    public UserInfo getUserInfo(String username) {
        return infos.get(username);
    }

}

您的控制器使用您从外部应用程序收到的用户信息填充 UserAuthenticationInfoService 服务。

然后实现一个自定义的 UserDetailsS​​ervice 来访问这些信息。

public class CustomerUserDetailsService implements UserDetailsService {
    @Autowired
    private UserAuthenticationInfoService service;

     public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException, DataAccessException {
        UserInfo info = service.getUserInfo(userName);
        return new User(info.getUsername(), info.getPassword(), info.getAuthorities());
    }
}

并设置 spring 安全上下文以使用此 UserDetailsS​​ervice(您可以在 spring 安全文档中找到它)

于 2012-09-11T10:50:26.313 回答