Spring安全性仅用于授权。外部认证

时间:2012-09-11 09:35:13

标签: spring security authentication authorization external

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

等网址的访问权限
<intercept-url pattern="/myprotectedpage*" access="hasRole('rightrole')" />

我阅读了一些关于PRE_AUTH_FILTER和UserDetailsS​​ervice的教程,但我无法理解。 Spring Security的应用程序生命周期是什么?涉及哪些课程? 我需要一些完整的工作样本。

3 个答案:

答案 0 :(得分:1)

有很多关于同样的东西,只需要谷歌正确。

无论如何,迄今为止我发现的最好的(几乎所有的春天)都是Krams,这是基本弹簧安全的一个。

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. Spring By Example
  2. MK Young
  3. And SpringSource Site itself
  4. 修改

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

    我的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作为我的身份验证提供程序,它是类com.indyaah.service.LoginService的bean。

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

    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");
            }
    
        }
    }
    

    注意这里的2件事。

    1. 我得到了用户详细信息(在我的情况下来自DB,你的可能是差异。)并将它放在一个新的org.springframework.security.core.userdetails.User对象下,然后由该方法返回以弹出安全性。
    2. 当然,(我根据我的数据库架构与DB分开加载,你的场景可能会有所不同),并通过相同的User对象将其传递给spring security。

答案 1 :(得分:0)

您可以实施自己的自定义AuthenticationManager和自定义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覆盖身份验证并添加外部逻辑时:

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;
        }

    }

答案 2 :(得分: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安全文档中找到它)