Spring Security Saml configuration error with OKTA

跟風遠走 提交于 2021-01-27 14:47:37

问题


Might be the question is already answered before but I cant find any answer for my problem so I ask you my question.

I am trying to implement SAML2 based SSO with OKTA. For that purpose I created a dev account at oktapreviw. I downloaded spring securty saml2 example at this link http://projects.spring.io/spring-security-saml/#quick-start and by using this link https://docs.spring.io/spring-security-saml/docs/1.0.x/reference/html/chapter-idp-guide.html#d5e1816

I adopted my configuration and this is working perfectly on localhost

Now we come to real world and I tried to configure on test environment. On test environement we have linux server with HAProxy installed as web server and behind haproxy we have our Service provider running on Tomcat.

For the time being we are using autosigned certificate for our haproxy. But now my project is not working and it gives me error :

org.springframework.security.authentication.AuthenticationServiceException: Incoming SAML message is invalid

My configuration in SecurityContext.xml is :

<!-- Enable auto-wiring -->
<context:annotation-config/>

<!-- Scan for auto-wiring classes in spring saml packages -->
<context:component-scan base-package="org.springframework.security.saml"/>

<!-- Unsecured pages -->
<security:http security="none" pattern="/favicon.ico"/>
<security:http security="none" pattern="/images/**"/>
<security:http security="none" pattern="/css/**"/>
<security:http security="none" pattern="/logout.jsp"/>
<security:http security="none" pattern="/ids/serveur/**"/>
<security:http security="none" pattern="/ids/geoportal/**"/>
<security:http security="none" pattern="/rest/static/**"/>

<!-- Security for the administration UI -->
<security:http pattern="/saml/web/**" use-expressions="false">
    <security:access-denied-handler error-page="/saml/web/metadata/login"/>
    <security:form-login login-processing-url="/saml/web/login" login-page="/saml/web/metadata/login" default-target-url="/saml/web/metadata"/>
    <security:intercept-url pattern="/saml/web/metadata/login" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
    <security:intercept-url pattern="/saml/web/**" access="ROLE_ADMIN"/>
    <security:custom-filter before="FIRST" ref="metadataGeneratorFilter"/>
</security:http>

<!-- Secured pages with SAML as entry point -->
<security:http entry-point-ref="samlEntryPoint" use-expressions="false">
    <security:intercept-url pattern="/**" access="IS_AUTHENTICATED_FULLY"/>
    <security:custom-filter before="FIRST" ref="metadataGeneratorFilter"/>
    <security:custom-filter after="BASIC_AUTH_FILTER" ref="samlFilter"/>
</security:http>

<!-- Filters for processing of SAML messages -->
<bean id="samlFilter" class="org.springframework.security.web.FilterChainProxy">
    <security:filter-chain-map request-matcher="ant">
        <security:filter-chain pattern="/saml/login/**" filters="samlEntryPoint"/>
        <security:filter-chain pattern="/saml/logout/**" filters="samlLogoutFilter"/>
        <security:filter-chain pattern="/saml/metadata/**" filters="metadataDisplayFilter"/>
        <security:filter-chain pattern="/saml/SSO/**" filters="samlWebSSOProcessingFilter"/>
        <security:filter-chain pattern="/saml/SSOHoK/**" filters="samlWebSSOHoKProcessingFilter"/>
        <security:filter-chain pattern="/saml/SingleLogout/**" filters="samlLogoutProcessingFilter"/>
        <security:filter-chain pattern="/saml/discovery/**" filters="samlIDPDiscovery"/>
    </security:filter-chain-map>
</bean>

<!-- Handler deciding where to redirect user after successful login -->
<bean id="successRedirectHandler"
      class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler">
    <property name="defaultTargetUrl" value="/"/>
</bean>

<!-- Use the following for interpreting RelayState coming from unsolicited response as redirect URL:
<bean id="successRedirectHandler" class="org.springframework.security.saml.SAMLRelayStateSuccessHandler">
   <property name="defaultTargetUrl" value="/" />
</bean>
-->

<!-- Handler deciding where to redirect user after failed login -->
<bean id="failureRedirectHandler"
      class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
    <property name="useForward" value="true"/>
    <property name="defaultFailureUrl" value="/error.jsp"/>
</bean>

<!-- Handler for successful logout -->
<bean id="successLogoutHandler" class="org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler">
    <property name="defaultTargetUrl" value="/logout.jsp"/>
</bean>

<security:authentication-manager alias="authenticationManager">
    <!-- Register authentication manager for SAML provider -->
    <security:authentication-provider ref="samlAuthenticationProvider"/>
    <!-- Register authentication manager for administration UI -->
    <security:authentication-provider>
        <security:user-service id="adminInterfaceService">
            <security:user name="admin" password="admin" authorities="ROLE_ADMIN"/>
        </security:user-service>
    </security:authentication-provider>
</security:authentication-manager>

<!-- Logger for SAML messages and events -->
<bean id="samlLogger" class="org.springframework.security.saml.log.SAMLDefaultLogger"/>

<!-- Central storage of cryptographic keys -->
<bean id="keyManager" class="org.springframework.security.saml.key.JKSKeyManager">
    <constructor-arg value="classpath:security/samlKeystore.jks"/>
    <constructor-arg type="java.lang.String" value="nalle123"/>
    <constructor-arg>
        <map>
            <entry key="apollo" value="nalle123"/>
        </map>
    </constructor-arg>
    <constructor-arg type="java.lang.String" value="apollo"/>
</bean>

<!-- Entry point to initialize authentication, default values taken from properties file -->
<bean id="samlEntryPoint" class="org.springframework.security.saml.SAMLEntryPoint">
    <property name="defaultProfileOptions">
        <bean class="org.springframework.security.saml.websso.WebSSOProfileOptions">
            <property name="includeScoping" value="false"/>
        </bean>
    </property>
</bean>

<!-- IDP Discovery Service -->
<bean id="samlIDPDiscovery" class="org.springframework.security.saml.SAMLDiscovery">
    <property name="idpSelectionPath" value="/WEB-INF/security/idpSelection.jsp"/>
</bean>

<!-- Filter automatically generates default SP metadata -->
<bean id="metadataGeneratorFilter" class="org.springframework.security.saml.metadata.MetadataGeneratorFilter">
    <constructor-arg>
        <bean class="org.springframework.security.saml.metadata.MetadataGenerator">
            <property name="extendedMetadata">
                <bean class="org.springframework.security.saml.metadata.ExtendedMetadata">
                    <property name="idpDiscoveryEnabled" value="false"/>
                </bean>
            </property>
        </bean>
    </constructor-arg>
</bean>

<!-- The filter is waiting for connections on URL suffixed with filterSuffix and presents SP metadata there -->
<bean id="metadataDisplayFilter" class="org.springframework.security.saml.metadata.MetadataDisplayFilter"/>

<!-- Configure HTTP Client to accept certificates from the keystore for HTTPS verification -->

<bean class="org.springframework.security.saml.trust.httpclient.TLSProtocolConfigurer">
    <property name="sslHostnameVerification" value="default"/>
</bean>


<!-- IDP Metadata configuration - paths to metadata of IDPs in circle of trust is here -->
<bean id="metadata" class="org.springframework.security.saml.metadata.CachingMetadataManager">
    <constructor-arg>
        <list>
            <!-- Example of classpath metadata with Extended Metadata -->
            <bean class="org.springframework.security.saml.metadata.ExtendedMetadataDelegate">
                <constructor-arg>
                    <bean class="org.opensaml.saml2.metadata.provider.ResourceBackedMetadataProvider">
                        <constructor-arg>
                            <bean class="java.util.Timer"/>
                        </constructor-arg>
                        <constructor-arg>
                            <bean class="org.opensaml.util.resource.ClasspathResource">
                                <constructor-arg value="/metadata/okta.xml"/>
                            </bean>
                        </constructor-arg>
                        <property name="parserPool" ref="parserPool"/>
                    </bean>
                </constructor-arg>
                <constructor-arg>
                    <bean class="org.springframework.security.saml.metadata.ExtendedMetadata">
                    </bean>
                </constructor-arg>
            </bean>
            <!-- Example of HTTP metadata without Extended Metadata -->
            <bean class="org.opensaml.saml2.metadata.provider.HTTPMetadataProvider">
                <!-- URL containing the metadata -->
                <constructor-arg>
                    <value type="java.lang.String">https://dev-880700.oktapreview.com/app/exkdlhbscqPei3k6d0h7/sso/saml/metadata</value>
                </constructor-arg>
                <!-- Timeout for metadata loading in ms -->
                <constructor-arg>
                    <value type="int">15000</value>
                </constructor-arg>
                <property name="parserPool" ref="parserPool"/>
            </bean>
            <!-- Example of file system metadata without Extended Metadata -->
            <!--
            <bean class="org.opensaml.saml2.metadata.provider.FilesystemMetadataProvider">
                <constructor-arg>
                    <value type="java.io.File">/usr/local/metadata/idp.xml</value>
                </constructor-arg>
                <property name="parserPool" ref="parserPool"/>
            </bean>
            -->
        </list>
    </constructor-arg>
    <!-- OPTIONAL used when one of the metadata files contains information about this service provider -->
    <!-- <property name="hostedSPName" value=""/> -->
    <!-- OPTIONAL property: can tell the system which IDP should be used for authenticating user by default. -->
    <!-- <property name="defaultIDP" value="http://localhost:8080/opensso"/> -->
</bean>

<!-- SAML Authentication Provider responsible for validating of received SAML messages -->
<bean id="samlAuthenticationProvider" class="org.springframework.security.saml.SAMLAuthenticationProvider">
    <!-- OPTIONAL property: can be used to store/load user data after login -->
    <!--
    <property name="userDetails" ref="bean" />
    -->
</bean>

<!-- Provider of default SAML Context -->
<bean id="contextProvider" class="org.springframework.security.saml.context.SAMLContextProviderImpl"/>

<!-- Processing filter for WebSSO profile messages -->
<bean id="samlWebSSOProcessingFilter" class="org.springframework.security.saml.SAMLProcessingFilter">
    <property name="authenticationManager" ref="authenticationManager"/>
    <property name="authenticationSuccessHandler" ref="successRedirectHandler"/>
    <property name="authenticationFailureHandler" ref="failureRedirectHandler"/>
</bean>

<!-- Processing filter for WebSSO Holder-of-Key profile -->
<bean id="samlWebSSOHoKProcessingFilter" class="org.springframework.security.saml.SAMLWebSSOHoKProcessingFilter">
    <property name="authenticationManager" ref="authenticationManager"/>
    <property name="authenticationSuccessHandler" ref="successRedirectHandler"/>
    <property name="authenticationFailureHandler" ref="failureRedirectHandler"/>
</bean>

<!-- Logout handler terminating local session -->
<bean id="logoutHandler"
      class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler">
    <property name="invalidateHttpSession" value="false"/>
</bean>

<!-- Override default logout processing filter with the one processing SAML messages -->
<bean id="samlLogoutFilter" class="org.springframework.security.saml.SAMLLogoutFilter">
    <constructor-arg index="0" ref="successLogoutHandler"/>
    <constructor-arg index="1" ref="logoutHandler"/>
    <constructor-arg index="2" ref="logoutHandler"/>
</bean>

<!-- Filter processing incoming logout messages -->
<!-- First argument determines URL user will be redirected to after successful global logout -->
<bean id="samlLogoutProcessingFilter" class="org.springframework.security.saml.SAMLLogoutProcessingFilter">
    <constructor-arg index="0" ref="successLogoutHandler"/>
    <constructor-arg index="1" ref="logoutHandler"/>
</bean>

<!-- Class loading incoming SAML messages from httpRequest stream -->
<bean id="processor" class="org.springframework.security.saml.processor.SAMLProcessorImpl">
    <constructor-arg>
        <list>
            <ref bean="redirectBinding"/>
            <ref bean="postBinding"/>
            <ref bean="artifactBinding"/>
            <ref bean="soapBinding"/>
            <ref bean="paosBinding"/>
        </list>
    </constructor-arg>
</bean>

<!-- SAML 2.0 WebSSO Assertion Consumer -->
<bean id="webSSOprofileConsumer" class="org.springframework.security.saml.websso.WebSSOProfileConsumerImpl"/>

<!-- SAML 2.0 Holder-of-Key WebSSO Assertion Consumer -->
<bean id="hokWebSSOprofileConsumer" class="org.springframework.security.saml.websso.WebSSOProfileConsumerHoKImpl"/>

<!-- SAML 2.0 Web SSO profile -->
<bean id="webSSOprofile" class="org.springframework.security.saml.websso.WebSSOProfileImpl"/>

<!-- SAML 2.0 Holder-of-Key Web SSO profile -->
<bean id="hokWebSSOProfile" class="org.springframework.security.saml.websso.WebSSOProfileConsumerHoKImpl"/>

<!-- SAML 2.0 ECP profile -->
<bean id="ecpprofile" class="org.springframework.security.saml.websso.WebSSOProfileECPImpl"/>

<!-- SAML 2.0 Logout Profile -->
<bean id="logoutprofile" class="org.springframework.security.saml.websso.SingleLogoutProfileImpl"/>

<!-- Bindings, encoders and decoders used for creating and parsing messages -->
<bean id="postBinding" class="org.springframework.security.saml.processor.HTTPPostBinding">
    <constructor-arg ref="parserPool"/>
    <constructor-arg ref="velocityEngine"/>
</bean>

<bean id="redirectBinding" class="org.springframework.security.saml.processor.HTTPRedirectDeflateBinding">
    <constructor-arg ref="parserPool"/>
</bean>

<bean id="artifactBinding" class="org.springframework.security.saml.processor.HTTPArtifactBinding">
    <constructor-arg ref="parserPool"/>
    <constructor-arg ref="velocityEngine"/>
    <constructor-arg>
        <bean class="org.springframework.security.saml.websso.ArtifactResolutionProfileImpl">
            <constructor-arg>
                <bean class="org.apache.commons.httpclient.HttpClient">
                    <constructor-arg>
                        <bean class="org.apache.commons.httpclient.MultiThreadedHttpConnectionManager"/>
                    </constructor-arg>
                </bean>
            </constructor-arg>
            <property name="processor">
                <bean class="org.springframework.security.saml.processor.SAMLProcessorImpl">
                    <constructor-arg ref="soapBinding"/>
                </bean>
            </property>
        </bean>
    </constructor-arg>
</bean>

<bean id="soapBinding" class="org.springframework.security.saml.processor.HTTPSOAP11Binding">
    <constructor-arg ref="parserPool"/>
</bean>

<bean id="paosBinding" class="org.springframework.security.saml.processor.HTTPPAOS11Binding">
    <constructor-arg ref="parserPool"/>
</bean>

<!-- Initialization of OpenSAML library-->
<bean class="org.springframework.security.saml.SAMLBootstrap"/>

<!-- Initialization of the velocity engine -->
<bean id="velocityEngine" class="org.springframework.security.saml.util.VelocityFactory" factory-method="getEngine"/>

<!-- 
    XML parser pool needed for OpenSAML parsing

    WARNING: If customizing a ParserPool implementation See https://shibboleth.net/community/advisories/secadv_20131213.txt
             Specifically the following should be explicitly set to avoid exploits:

             1) set pool property 'expandEntityReferences' to 'false'
             2) set feature 'javax.xml.XMLConstants.FEATURE_SECURE_PROCESSING' to true
             3) set feature 'http://apache.org/xml/features/disallow-doctype-decl' to true. This is a Xerces-specific feature,
                including derivatives such as the internal JAXP implementations supplied with the Oracle and OpenJDK JREs. For
                other JAXP implementations, consult the documentation for the implementation for guidance on how to achieve a
                similar configuration.
-->
<bean id="parserPool" class="org.opensaml.xml.parse.StaticBasicParserPool" init-method="initialize"/>

<bean id="parserPoolHolder" class="org.springframework.security.saml.parser.ParserPoolHolder"/>

Now I have two assumptions :

  1. Whether my auto signed certificate is creating problem for me
  2. Or I have to declare teh configuration of this auto signed certificate somewhere

For information : I declared the project in haproxy and my pages with no security are accessible via HAProxy. I also tested the solution given in post Spring-SAML : Incoming SAML message is invalid , but still I have same problem.

Thanks a lot for your help

Ayyaz

EDIT 05 Féverier :

I tested and here is the information which I can extract the log file :

  • Verification successful for URI "#id51308797331193271793210762"
  • The Reference has Type
  • Signature validated with key from supplied credential
  • Signature validation using candidate credential was successful
  • Successfully verified signature using KeyInfo-derived credential
  • Attempting to establish trust of KeyInfo-derived credential
  • Failed to validate untrusted credential against trusted key
  • Successfully validated untrusted credential against trusted key
  • Successfully established trust of KeyInfo-derived credential
  • Validation of protocol message signature succeeded, message type: {urn:oasis:names:tc:SAML:2.0:protocol}Response
  • Authentication via protocol message signature succeeded for context issuer entity ID http://www.okta.com/exkdlhbscqPei3k6d0h7
  • Successfully decoded message.
  • Checking SAML message intended destination endpoint against receiver endpoint
  • Intended message destination endpoint: https://dev-XXX.XXX.net/accessids/saml/SSO
  • Actual message receiver endpoint: http://dev-XXX.XXX.net/accessids/saml/SSO
  • SAML message intended destination endpoint 'https://dev-XXX.XXX.net/accessids/saml/SSO' did not match the recipient endpoint 'http://dev-XXX.XXX.net/accessids/saml/SSO'
  • Incoming SAML message is invalid org.opensaml.xml.security.SecurityException: SAML message intended destination endpoint did not match recipient endpoint at org.opensaml.common.binding.decoding.BaseSAMLMessageDecoder.checkEndpointURI(BaseSAMLMessageDecoder.java:217) at org.opensaml.saml2.binding.decoding.BaseSAML2MessageDecoder.decode(BaseSAML2MessageDecoder.java:72) at org.springframework.security.saml.processor.SAMLProcessorImpl.retrieveMessage(SAMLProcessorImpl.java:105) at org.springframework.security.saml.processor.SAMLProcessorImpl.retrieveMessage(SAMLProcessorImpl.java:172) at org.springframework.security.saml.SAMLProcessingFilter.attemptAuthentication(SAMLProcessingFilter.java:77) at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:195) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:192) at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:166) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:87) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) at org.springframework.security.saml.metadata.MetadataGeneratorFilter.doFilter(MetadataGeneratorFilter.java:87) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:192) at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:160) at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346) at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:259) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:212) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:94) at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:504) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:141) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79) at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:620) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:502) at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1132) at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:684) at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1533) at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1489) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)

回答1:


The message endpoints don't match:

SAML message intended destination endpoint did not match recipient endpoint

It's expecting to send the message to (Intended message destination endpoint):

https://dev-XXX.XXX.net/accessids/saml/SSO

but it's being asked to send to (Actual message receiver endpoint):

http://dev-XXX.XXX.net/accessids/saml/SSO

So the metadata is probably saying it should go to https but it's being sent to http instead. The URLs have to match exactly.

Check your haproxy isn't changing it from https to http.




回答2:


Hello all and thanks for your help,

Finally I was able to solve the problem so I decided to post this on forum to help other beginners like me who can face similar sort of problems.

In the start I was just working with a single tomcat and I created the application on OKTA. I downloaded and configured metadata xml file. In the OKTA project for SSO URL and Audience restriction URL as http://localhost:8080/....

The project worked fine. Then I deployed the project with HAProxy in our test environment and constantly I had an error (as explianed above) :

org.springframework.security.authentication.AuthenticationServiceException: Incoming SAML message is invalid

For information I modified the URL in OKTA Project and I used the URL of HAProxy which was https://dev-xxx.xxx.grdf.net

I had restriction to have development on Windows server and our testing and production environments are red hat based. So I installed Apache on my windows machine and I created an auto signed certificate. SO I tried to simulate my testing environment. In OKTA project I used the URL https://localhost/ and I produced similar error which I had with HAProxy.

When I analysed log I got an impression that as had not declared

entityBaseURL

On first call application downloaded metadata with the URL that my endpoint did not know. So I added this line which resolved my problem on development machine :

<property name="entityBaseURL" value="https://localhost/XXX"/>

But again while I tested on HAProxy I again got an error which was :

SAML message intended destination endpoint 'https://dev-XXX.XXX.net/accessids/saml/SSO' did not match the recipient endpoint 'http://dev-XXX.XXX.net/accessids/saml/SSO' Incoming SAML message is invalid org.opensaml.xml.security.SecurityException: SAML message intended destination endpoint did not match recipient endpoint at org.opensaml.common.binding.decoding.BaseSAMLMessageDecoder.checkEndpointURI(BaseSAMLMessageDecoder.java:217)

(which I also posted yesterday)

Then while look at documentation I found a chapter about loadbalancer so I modified my configuration and I used SAMLContextProviderLB at the place of SAMLContextProviderImpl. So I used this configuration :

<bean id="contextProvider" class="org.springframework.security.saml.context.SAMLContextProviderLB">
        <property name="scheme" value="https"/>
        <property name="serverName" value="dev-XXX.XXX.net"/>
        <property name="serverPort" value="443"/>
        <property name="includeServerPortInRequestURL" value="false"/>
        <property name="contextPath" value="/XXXXX"/>
    </bean>

I also left entityBaseURL in my XML file :

<bean id="metadataGeneratorFilter" class="org.springframework.security.saml.metadata.MetadataGeneratorFilter">
        <constructor-arg>
            <bean class="org.springframework.security.saml.metadata.MetadataGenerator">
                <property name="entityBaseURL" value="https://dev-XXX.XXX.net/XXXXXX"/>
            </bean>
        </constructor-arg>
    </bean>

These two parameters made my configuration executable in testing environment. If you have any suggestions, questions please free to contact me.

Have a great day



来源:https://stackoverflow.com/questions/48580034/spring-security-saml-configuration-error-with-okta

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!