WCF身份验证一般常见的方式有:自定义用户名及密码验证、X509证书验证、ASP.NET成员资格(membership)验证、SOAP Header验证、Windows集成验证、WCF身份验证服务(AuthenticationService),这些验证方式其实网上都有相关的介绍文章,我这里算是一个总结吧,顺便对于一些注意细节进行说明,以便大家能更好的掌握这些知识。

第一种:自定义用户名及密码验证(需要借助X509证书)

由于该验证需要借助于X509证书,所以我们需要先创建一个证书,可以利用MS自带的makecert.exe程序来制作测试用证书,使用步骤:请依次打开开始->Microsoft Visual Studio 2010(VS菜单,版本不同,名称有所不同)->Visual Studio Tools->Visual Studio 命令提示,然后执行以下命令:

makecert -r -pe -n "CN=ZwjCert" -ss TrustedPeople -sr LocalMachine -sky exchange

上述命令中除了我标粗的部份可改成你实际的请求外(为证书名称),其余的均可以保持不变,命令的意思是:创建一个名为ZwjCert的证书将将其加入到本地计算机的受信任人区域中。

如果需要查看该证书,那么可以通过MMC控制台查询证书,具体操作步骤如下:

运行->MMC,第一次打开Windows没有给我们准备好直接的管理证书的入口,需要自行添加,添加方法如下:

1. 在控制台菜单,文件→添加/删除管理单元→添加按钮→选”证书”→添加→选”我的用户账户”→关闭→确定
2. 在控制台菜单,文件→添加/删除管理单元→添加按钮→选”证书”→添加→选”计算机账户”→关闭→确定

这样MMC中左边就有菜单了,然后依次展开:证书(本地计算机)->受信任人->证书,最后就可以在右边的证书列表中看到自己的证书了,如下图示:

证书创建好,我们就可以开始编码了,本文主要讲的就是WCF,所以我们首先定义一个WCF服务契约及服务实现类(后面的各种验证均采用该WCF服务),我这里直接采用默认的代码,如下:

要实现用户名及密码验证,就需要定义一个继承自UserNamePasswordValidator的用户名及密码验证器类CustomUserNameValidator,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
namespace WcfAuthentications
{
    public class CustomUserNameValidator : UserNamePasswordValidator
    {
 
        public override void Validate(string userName, string password)
        {
            if (null == userName || null == password)
            {
                throw new ArgumentNullException();
            }
            if (userName != "admin" && password != "wcf.admin"//这里可依实际情况下实现用户名及密码判断
            {
                throw new System.IdentityModel.Tokens.SecurityTokenException("Unknown Username or Password");
            }
 
        }
    }
}

代码很简单,只是重写其Validate方法,下面就是将创建WCF宿主,我这里采用控制台程序

代码部份:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
namespace WcfHost
{
    class Program
    {
        static void Main(string[] args)
        {
            using (var host = new ServiceHost(typeof(Service1)))
            {
                host.Opened += delegate
                {
                    Console.WriteLine("Service1 Host已开启!");
                };
                host.Open();
                Console.ReadKey();
            }
        }
    }
}

APP.CONFIG部份(这是重点,可以使用WCF配置工具来进行可视化操作配置,参见:http://www.cnblogs.com/Moosdau/archive/2011/04/17/2019002.html):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<system.serviceModel>
  <bindings>
    <wsHttpBinding>
      <binding name="Service1Binding">
        <security mode="Message">
          <message clientCredentialType="UserName" />
        </security>
      </binding>
    </wsHttpBinding>
  </bindings>
  <services>
    <service behaviorConfiguration="Service1Behavior" name="WcfAuthentications.Service1">
      <endpoint address="" binding="wsHttpBinding" bindingConfiguration="Service1Binding"
        contract="WcfAuthentications.IService1">
        <identity>
          <dns value="ZwjCert" />
        </identity>
      </endpoint>
      <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
      <host>
        <baseAddresses>
          <add baseAddress="http://localhost:8732/WcfAuthentications/Service1/" />
        </baseAddresses>
      </host>
    </service>
  </services>
  <behaviors>
    <serviceBehaviors>
      <behavior name="Service1Behavior">
        <serviceMetadata httpGetEnabled="true" />
        <serviceDebug includeExceptionDetailInFaults="false" />
        <serviceCredentials>
          <serviceCertificate findValue="ZwjCert" x509FindType="FindBySubjectName" storeLocation="LocalMachine" storeName="TrustedPeople" />
          <userNameAuthentication userNamePasswordValidationMode="Custom"
            customUserNamePasswordValidatorType="WcfAuthentications.CustomUserNameValidator,WcfAuthentications" />
        </serviceCredentials>
      </behavior>
    </serviceBehaviors>
  </behaviors>
</system.serviceModel>

这里面有几个需要注意的点:

1.<dns value="ZwjCert" />与<serviceCertificate findValue="ZwjCert" ..>中的value必需都为证书的名称,即:ZwjCert;

2.Binding节点中需配置security节点,message子节点中的clientCredentialType必需设为:UserName;

3.serviceBehavior节点中,需配置serviceCredentials子节点,其中serviceCertificate 中各属性均需与证书相匹配,userNameAuthentication的userNamePasswordValidationMode必需为Custom,customUserNamePasswordValidatorType为上面自定义的用户名及密码验证器类的类型及其程序集

最后就是在客户端使用了,先引用服务,然后看下App.Config,并进行适当的修改,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<system.serviceModel>
    <bindings>
        <wsHttpBinding>
            <binding name="WSHttpBinding_IService1" >
                <security mode="Message">
                    <transport clientCredentialType="Windows" proxyCredentialType="None"
                        realm="" />
                    <message clientCredentialType="UserName" negotiateServiceCredential="true"
                        algorithmSuite="Default" />
                </security>
            </binding>
        </wsHttpBinding>
    </bindings>
    <client>
        <endpoint address="http://localhost:8732/WcfAuthentications/Service1/"
            binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_IService1"
            contract="ServiceReference1.IService1" name="WSHttpBinding_IService1">
            <identity>
                <dns value="ZwjCert" />
            </identity>
        </endpoint>
    </client>
</system.serviceModel>

为了突出重点,我这里对Binding节点进行了精简,去掉了许多的属性配置,仅保留重要的部份,如:security节点,修改其endpoint下面的identity中<dns value="ZwjCert" />,这里的value与服务中所说的相同节点相同,就是证书名称,如果不相同,那么就会报错,具体的错误消息大家可以自行试下,我这里限于篇幅内容就不贴出来了。

客户端使用服务代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
namespace WCFClient
{
    class Program
    {
        static void Main(string[] args)
        {
            using (var proxy = new ServiceReference1.Service1Client())
            {
                proxy.ClientCredentials.UserName.UserName = "admin";
                proxy.ClientCredentials.UserName.Password = "wcf.admin";
                string result = proxy.GetData(1);
                Console.WriteLine(result);
                var compositeObj = proxy.GetDataUsingDataContract(new CompositeType() { BoolValue = true, StringValue = "test" });
                Console.WriteLine(SerializerToJson(compositeObj));
            }
            Console.ReadKey();
        }
 
        /// <summary>
        /// 序列化成JSON字符串
        /// </summary>
        static string SerializerToJson<T>(T obj) where T:class
        {
            var serializer = new DataContractJsonSerializer(typeof(T));
            var stream = new MemoryStream();
            serializer.WriteObject(stream,obj);
 
            byte[] dataBytes = new byte[stream.Length];
 
            stream.Position = 0;
            stream.Read(dataBytes, 0, (int)stream.Length);
            string dataString = Encoding.UTF8.GetString(dataBytes);
            return dataString;
        }
    }
}

运行结果如下图示:

  

如果不传入用户名及密码或传入不正确的用户名及密码,均会报错:

第二种:X509证书验证


首先创建一个证书,我这里就用上面创建的一个证书:ZwjCert;由于服务器端及客户端均需要用到该证书,所以需要导出证书,在客户端的电脑上导入该证书,以便WCF可进行验证。

WCF服务契约及服务实现类与第一种方法相同,不再重贴代码。

WCF服务器配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<system.serviceModel>
  <bindings>
    <wsHttpBinding>
      <binding name="Service1Binding">
        <security mode="Message">
          <message clientCredentialType="Certificate" />
        </security>
      </binding>
    </wsHttpBinding>
  </bindings>
  <services>
    <service behaviorConfiguration="Service1Behavior" name="WcfAuthentications.Service1">
      <endpoint address="" binding="wsHttpBinding" bindingConfiguration="Service1Binding"
        contract="WcfAuthentications.IService1">
      </endpoint>
      <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
      <host>
        <baseAddresses>
          <add baseAddress="http://127.0.0.1:8732/WcfAuthentications/Service1/" />
        </baseAddresses>
      </host>
    </service>
  </services>
  <behaviors>
    <serviceBehaviors>
      <behavior name="Service1Behavior">
        <serviceMetadata httpGetEnabled="true" />
        <serviceDebug includeExceptionDetailInFaults="false" />
        <serviceCredentials>
          <serviceCertificate findValue="ZwjCert" x509FindType="FindBySubjectName" storeLocation="LocalMachine" storeName="TrustedPeople" />
          <clientCertificate>
            <authentication certificateValidationMode="None"/>
          </clientCertificate>
        </serviceCredentials>
      </behavior>
    </serviceBehaviors>
  </behaviors>
</system.serviceModel>

这里需注意如下几点:

1.<message clientCredentialType="Certificate" />clientCredentialType设为:Certificate;

2.需配置serviceCredentials节点,其中serviceCertificate 中各属性均需与证书相匹配,clientCertificate里面我将authentication.certificateValidationMode="None",不设置采用默认值其实也可以;

客户端引用服务,自动生成如下配置信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<system.serviceModel>
    <bindings>
        <wsHttpBinding>
            <binding name="WSHttpBinding_IService1">
                <security mode="Message">
                    <transport clientCredentialType="Windows" proxyCredentialType="None"
                        realm="" />
                    <message clientCredentialType="Certificate" negotiateServiceCredential="true"
                        algorithmSuite="Default" />
                </security>
            </binding>
        </wsHttpBinding>
    </bindings>
    <client>
        <endpoint address="http://127.0.0.1:8732/WcfAuthentications/Service1/"
            binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_IService1"
            contract="ServiceReference1.IService1" name="WSHttpBinding_IService1" behaviorConfiguration="Service1Nehavior">
            <identity>
                <certificate encodedValue="AwAAAAEAAAAUAAAAkk2avjNCItzUlS2+Xj66ZA2HBZYgAAAAAQAAAOwBAAAwggHoMIIBVaADAgECAhAIAOzFvLxLuUhHJRwHUUh9MAkGBSsOAwIdBQAwEjEQMA4GA1UEAxMHWndqQ2VydDAeFw0xNTEyMDUwMjUyMTRaFw0zOTEyMzEyMzU5NTlaMBIxEDAOBgNVBAMTB1p3akNlcnQwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALfGfsiYpIVKu3gPJl790L13+CZWt6doePZHmcjMl+xPQKIR2fDvsCq9ZxzapDgiG4T3mgcVKUv55DBiuHcpXDvXt28m49AjdKwp924bOGKPM56eweKDCzYfLxy5SxaZfA9qjUhnPq3kGu1lfWjXbsp1rKI1UhKJg5b2j0V7AOC3AgMBAAGjRzBFMEMGA1UdAQQ8MDqAEH/MEXV8FHNLtxvllQ5SMbihFDASMRAwDgYDVQQDEwdad2pDZXJ0ghAIAOzFvLxLuUhHJRwHUUh9MAkGBSsOAwIdBQADgYEAdBtBNTK/Aj3woH2ts6FIU3nh7FB2tKQ9L3k6QVL+kCR9mHuqWtYFJTBKxzESN2t0If6muiktcO+C8iNwYpJpPzLAOMFMrTQhkO82gcdr9brQzMWPTraK1IS+GGH8QBIOTLx9zfV/iCIXxRub+Sq9dmRSQjKDeLeHWoE5I6FkQJg=" />
            </identity>
        </endpoint>
    </client>
  <behaviors>
    <endpointBehaviors>
      <behavior name="Service1Nehavior">
        <clientCredentials>
          <clientCertificate findValue="ZwjCert" x509FindType="FindBySubjectName" storeLocation="LocalMachine" storeName="TrustedPeople" />
        </clientCredentials>
      </behavior>
    </endpointBehaviors>
  </behaviors>
</system.serviceModel>

可以看出endpoint节点下的identity.certificate的encodedValue包含了加密的数据,另外需要手动增加clientCertificate配置信息,该信息表示证书在本地电脑存放的位置,当然也可以通过代码来动态指定,如:proxy.ClientCredentials.ClientCertificate.SetCertificate("ZwjCert", StoreLocation.LocalMachine, StoreName.My);

客户端使用服务代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
static void Main(string[] args)
{
    using (var proxy = new ServiceReference1.Service1Client())
    {
 
        //proxy.ClientCredentials.ClientCertificate.SetCertificate("ZwjCert", StoreLocation.LocalMachine, StoreName.My); //直接动态指定证书存储位置
        string result = proxy.GetData(1);
        Console.WriteLine(result);
        var compositeObj = proxy.GetDataUsingDataContract(new CompositeType() { BoolValue = true, StringValue = "test" });
        Console.WriteLine(SerializerToJson(compositeObj));
    }
    Console.ReadKey();
}

网上还有另类的针对X509证书验证,主要是采用了自定义的证书验证器类,有兴趣的可以参见这篇文章:http://www.cnblogs.com/ejiyuan/archive/2010/05/31/1748363.html

第三种:ASP.NET成员资格(membership)验证

由于该验证需要借助于X509证书,所以仍然需要创建一个证书(方法如第一种中创建证书方法相同):ZwjCert;

由于该种验证方法是基于ASP.NET的membership,所以需要创建相应的数据库及创建账号,创建数据库,请通过运行aspnet_regsql.exe向导来创建数据库及其相关的表,通过打开ASP.NET 网站管理工具(是一个自带的管理网站),并在上面创建角色及用户,用于后续的验证;

这里特别说明一下,若采用VS2013,VS上是没有自带的GUI按钮来启动该管理工具网站,需要通过如下命令来动态编译该网站:

1
2
cd C:\Program Files\IIS Express
iisexpress.exe /path:C:\Windows\Microsoft.NET\Framework\v4.0.30319\ASP.NETWebAdminFiles /vpath:/WebAdmin /port:12345 /clr:4.0 /ntlm

编译时若出现报错:“System.Configuration.StringUtil”不可访问,因为它受保护级别限制,请将WebAdminPage.cs中代码作如下修改:

1
2
3
4
5
6
7
8
//取消部份:
string appId = StringUtil.GetNonRandomizedHashCode(String.Concat(appPath, appPhysPath)).ToString("x", CultureInfo.InvariantCulture);
 
 
//新增加部份:
Assembly sysConfig = Assembly.LoadFile(@"C:\Windows\Microsoft.NET\Framework\v4.0.30319\System.Configuration.dll");
Type sysConfigType = sysConfig.GetType("System.Configuration.StringUtil");
string appId = ((int)sysConfigType.GetMethod("GetNonRandomizedHashCode").Invoke(nullnew object[] { String.Concat(appPath, appPhysPath), true })).ToString("x", CultureInfo.InvariantCulture);

这样就可以按照命令生生成的网址进行访问就可以了。如果像我一样,操作系统为:WINDOWS 10,那么不好意思,生成的网站虽然能够打开,但仍会报错:

遇到错误。请返回上一页并重试。

目前没有找到解决方案,网上有说ASP.NET网站管理工具在WIN10下不被支持,到底为何暂时无解,若大家有知道的还请分享一下(CSDN有别人的求问贴:http://bbs.csdn.net/topics/391819719),非常感谢,我这里就只好换台电脑来运行ASP.NET管理工具网站了。

WCF服务端配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<connectionStrings>
  <add name="SqlConn" connectionString="Server=.;Database=aspnetdb;Uid=sa;Pwd=www.zuowenjun.cn;"/>
</connectionStrings>
<system.web>
  <compilation debug="true" targetFramework="4.5" />
  <httpRuntime targetFramework="4.5"/>
  <membership defaultProvider="SqlMembershipProvider">
    <providers>
      <clear/>
      <add name="SqlMembershipProvider" type="System.Web.Security.SqlMembershipProvider" connectionStringName="SqlConn" applicationName="/" enablePasswordRetrieval="false" enablePasswordReset="false" requiresQuestionAndAnswer="false" requiresUniqueEmail="true" passwordFormat="Hashed"/>
    </providers>
  </membership>
</system.web>
<system.serviceModel>
  <behaviors>
    <serviceBehaviors>
      <behavior name="Service1Behavior">
        <serviceCredentials>
          <serviceCertificate findValue="ZwjCert" storeLocation="LocalMachine"
            storeName="TrustedPeople" x509FindType="FindBySubjectName" />
          <userNameAuthentication userNamePasswordValidationMode="MembershipProvider"
            membershipProviderName="SqlMembershipProvider" />
        </serviceCredentials>
         <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
        <serviceDebug includeExceptionDetailInFaults="false" />
      </behavior>
    </serviceBehaviors>
  </behaviors>
  <bindings>
    <wsHttpBinding>
      <binding name="Service1Binding">
        <security mode="Message">
          <message clientCredentialType="UserName"/>
        </security>
      </binding>
    </wsHttpBinding>
  </bindings>
  <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
<services>
  <service name="WcfService1.Service1" behaviorConfiguration="Service1Behavior">
    <endpoint address="" binding="wsHttpBinding" contract="WcfService1.IService1" bindingConfiguration="Service1Binding">
    </endpoint>
  </service>
</services>
</system.serviceModel>

这里需注意几点:

1.配置connectionString,连接到membership所需的数据库;

2.配置membership,增加SqlMembershipProvider属性配置;

3.配置serviceCredential,与第一种基本相同,不同的是userNameAuthentication的配置:userNamePasswordValidationMode="MembershipProvider",membershipProviderName="SqlMembershipProvider";

4.配置Binding节点<message clientCredentialType="UserName"/>,这与第一种相同;

客户端引用WCF服务,查看生成的配置文件内容,需确保Binding节点有以下配置信息:

1
2
3
<security mode="Message">
        <message clientCredentialType="UserName" />
 </security>

最后使用WCF服务,使用代码与第一种相同,唯一需要注意的是,传入的UserName和Password均为ASP.NET网站管理工具中创建的用户信息。

另外我们也可以采用membership+Form验证,利用ASP.NET的身份验证机制,要实现这种模式,是需要采用svc文件,并寄宿在IIS上,具体实现方法,参见:http://www.cnblogs.com/danielWise/archive/2011/01/30/1947912.html

WCF权限认证多种方式的更多相关文章

  1. SpringBoot之SpringSecurity权限注解在方法上进行权限认证多种方式

    前言 Spring Security支持方法级别的权限控制.在此机制上,我们可以在任意层的任意方法上加入权限注解,加入注解的方法将自动被Spring Security保护起来,仅仅允许特定的用户访问, ...

  2. Cognos权限认证CJP方式之用户密码加密

    在项目开发过程中,用户往往对系统的安全都有明确的要求,下面针对cognos门户认证用户密码如何加密来提供一个简单的wf 1Cognos权限认证方式:CJP 2Cognos用户数据库类型:Oracle ...

  3. ansible-playbook权限提升多种方式

    ansible-playbook 可以方便快速的批量执行部署和运维任务,对于不同的场景和服务器,需要使用不同的权限提升方式. 最佳实现:为了提高playbook的兼容性,跟功能没有直接关系的权限提升脚 ...

  4. Restful风格wcf调用4——权限认证

    写在前面 在前面的三篇文章,已经介绍了restful风格wcf,如何实现增删改查以及文件的上传下载操作.本篇文章将介绍一下,调用restful的权限认证的内容.在调用的接口,为了安全,总会需要对请求进 ...

  5. 比RBAC更好的权限认证方式(Auth类认证)

    Auth 类已经在ThinkPHP代码仓库中存在很久了,但是因为一直没有出过它的教程, 很少人知道它, 它其实比RBAC更方便 . RBAC是按节点进行认证的,如果要控制比节点更细的权限就有点困难了, ...

  6. thinkphp Auth认证类 比RBAC更好的权限认证方式(Auth类认证)

    thinkphp Auth认证类 比RBAC更好的权限认证方式(Auth类认证)    Auth 类已经在ThinkPHP代码仓库中存在很久了,但是因为一直没有出过它的教程, 很少人知道它, 它其实比 ...

  7. thinkphp 比RBAC更好的权限认证方式(Auth类认证)

    Auth 类已经在ThinkPHP代码仓库中存在很久了,但是因为一直没有出过它的教程, 很少人知道它, 它其实比RBAC更方便 . RBAC是按节点进行认证的,如果要控制比节点更细的权限就有点困难了, ...

  8. WCF权限控制

       前面写了 WCF账户密码认证, 实现了帐号密码认证, 接下来看看如何对方法的细粒度控制, 本文很大程度参考了 WCF安全之基于自定义声明授权策略, 这篇文章对原理讲得比较清楚, 而我这篇文章呢, ...

  9. (WCF初体验)WCF的认证和消息保护

    最近做WCF开发,有个需求是在服务端做认证,网上查资料了解到可以用UserName和Password 来做认证,只需要写好配置文件和在服务端写好验证类就行了,但是网上普遍的博文都是需要用证书,而我自己 ...

随机推荐

  1. TP5.1+Vue前后端分离实践

    配置: 主域名 www.demo.xyz 二级子域名 api.demo.xyz 列表项目其中api.demo.xyz加入了版本控制,使用的是URL路由传入方式 在route.php路由文件中配置,如下 ...

  2. 2019 Multi-University Training Contest 2 - 1008 - Harmonious Army - 最大流

    http://acm.hdu.edu.cn/showproblem.php?pid=6598 一开始就觉得是网络流,但是一直都不会怎么建图. 这里要考虑. 每一组边(u,v,a,b,c)建立如下的连接 ...

  3. 基于 Python 的自定义分页组件

    基于 Python 的自定义分页组件 分页是网页中经常用到的地方,所以将分页功能分出来,作为一个组件可以方便地使用. 分页实际上就是不同的 url ,通过这些 url 获取不同的数据. 业务逻辑简介 ...

  4. Hibernate快速入门之CRUD

    一.Hibernate的基本概念 前言 Hibernate是一个ORM框架,ORM是Object Relational Mapping的缩写,即对象关系映射,也就是将面向对象思想的编程语言与关系型数据 ...

  5. xcopy忽略文件 7zip打包

    xcopy src target /Y /r /d /k /f /s /exclude:%cd%\xcopy.config xcopy.config\assets\\tet\ 7z.exe a -tz ...

  6. uiautomator2 使用注意的地方

    uiautomator2项目地址:https://github.com/openatx/uiautomator2#basic-api-usages 下面记录一些自己在使用过程中的坑,仅供参考 1.通过 ...

  7. tensorflow函数介绍(3)

    tf.nn.softmax_cross_entropy_with_logits(logits,labels) #其中logits为神经网络最后一层输出,labels为实际的标签,该函数返回经过soft ...

  8. java File过滤文件的多种方法

    package com.qf.part1; import java.io.File; import java.io.FileFilter; import java.io.IOException; pu ...

  9. [NOIP2016][luogu]换教室[DP]

    [NOIP2016] Day1 T3 换教室 ——!x^n+y^n=z^n 题目描述 对于刚上大学的牛牛来说,他面临的第一个问题是如何根据实际情况申请合适的课程. 在可以选择的课程中,有 2n 节课程 ...

  10. 科匠中国深圳java面试笔试题