├── .gitignore
├── webapi.sln
├── webapi.sln.DotSettings.user
└── webapi
├── AutoFac
├── ContainerBuilerCommon.cs
└── Modules
│ └── LoggingModule.cs
├── Common
├── EncryptHelper.cs
├── Enums.cs
├── IdManager.cs
└── JWTHelper.cs
├── Configs
├── Config.cs
└── WebApiConfig.cs
├── ConnectionStrings.config
├── Entities
├── BaseEntity.cs
├── DB.cs
├── Permission.cs
├── Resource.cs
├── Role.cs
├── TestTable.cs
├── URM.cs
└── User.cs
├── Exceptions
├── KnownException.cs
└── WebApiExceptionFilterAttribute.cs
├── Global.asax
├── Global.asax.cs
├── Log4net.config
├── Microsoft.Owin.Host.HttpListener.dll
├── Microsoft.Owin.Hosting.dll
├── Microsoft.Owin.dll
├── Migrations
└── Configuration.cs
├── Owin.dll
├── Owin
└── Startup.cs
├── OwinHost.exe
├── Properties
└── AssemblyInfo.cs
├── Security
├── IdentityBasicAuthentication.cs
└── RBAuthorizeAttribute.cs
├── Services
├── AuthorizeService.cs
├── BaseService.cs
├── PermissionService.cs
└── ResourceService.cs
├── Web.Debug.config
├── Web.Release.config
├── Web.config
├── example
├── EFTestController.cs
├── ExceptionTestController.cs
├── IMyLog.cs
├── IOCTestController.cs
├── LogTestController.cs
├── SecurityTestController.cs
└── TestController.cs
├── packages.config
├── readme.txt
├── webapi.csproj
└── webapi.csproj.user
/.gitignore:
--------------------------------------------------------------------------------
1 | bin
2 | obj
3 | /packages
4 | .vs
5 | /webapi/log
6 | /webapiTests
7 |
--------------------------------------------------------------------------------
/webapi.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.27004.2008
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "webapi", "webapi\webapi.csproj", "{7A63DCA6-5F3F-440A-934E-A92C162C0E82}"
7 | EndProject
8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MyOwinSelfHost", "..\MyOwinSelfHost\MyOwinSelfHost.csproj", "{1B8731CF-BEE2-4650-849C-7E5CFB9C9A1C}"
9 | EndProject
10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "webapiTests", "webapiTests\webapiTests.csproj", "{AA7BEC9D-0524-49DA-BF74-4ACE836CEE6A}"
11 | EndProject
12 | Global
13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
14 | Debug|Any CPU = Debug|Any CPU
15 | Release|Any CPU = Release|Any CPU
16 | EndGlobalSection
17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
18 | {7A63DCA6-5F3F-440A-934E-A92C162C0E82}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
19 | {7A63DCA6-5F3F-440A-934E-A92C162C0E82}.Debug|Any CPU.Build.0 = Debug|Any CPU
20 | {7A63DCA6-5F3F-440A-934E-A92C162C0E82}.Release|Any CPU.ActiveCfg = Release|Any CPU
21 | {7A63DCA6-5F3F-440A-934E-A92C162C0E82}.Release|Any CPU.Build.0 = Release|Any CPU
22 | {1B8731CF-BEE2-4650-849C-7E5CFB9C9A1C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
23 | {1B8731CF-BEE2-4650-849C-7E5CFB9C9A1C}.Debug|Any CPU.Build.0 = Debug|Any CPU
24 | {1B8731CF-BEE2-4650-849C-7E5CFB9C9A1C}.Release|Any CPU.ActiveCfg = Release|Any CPU
25 | {1B8731CF-BEE2-4650-849C-7E5CFB9C9A1C}.Release|Any CPU.Build.0 = Release|Any CPU
26 | {AA7BEC9D-0524-49DA-BF74-4ACE836CEE6A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
27 | {AA7BEC9D-0524-49DA-BF74-4ACE836CEE6A}.Debug|Any CPU.Build.0 = Debug|Any CPU
28 | {AA7BEC9D-0524-49DA-BF74-4ACE836CEE6A}.Release|Any CPU.ActiveCfg = Release|Any CPU
29 | {AA7BEC9D-0524-49DA-BF74-4ACE836CEE6A}.Release|Any CPU.Build.0 = Release|Any CPU
30 | EndGlobalSection
31 | GlobalSection(SolutionProperties) = preSolution
32 | HideSolutionNode = FALSE
33 | EndGlobalSection
34 | GlobalSection(ExtensibilityGlobals) = postSolution
35 | SolutionGuid = {537610E8-BBB1-40B9-AF64-A2012ECEF865}
36 | EndGlobalSection
37 | EndGlobal
38 |
--------------------------------------------------------------------------------
/webapi.sln.DotSettings.user:
--------------------------------------------------------------------------------
1 |
2 | <AssemblyExplorer>
3 | <Assembly Path="D:\我的框架\webapi\packages\Microsoft.AspNet.WebApi.WebHost.5.2.3\lib\net45\System.Web.Http.WebHost.dll" />
4 | <Assembly Path="D:\我的框架\webapi\webapi\Bin\JWT.dll" />
5 | </AssemblyExplorer>
--------------------------------------------------------------------------------
/webapi/AutoFac/ContainerBuilerCommon.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using Autofac;
3 | using Autofac.Integration.WebApi;
4 | using webapi.AutoFac.Modules;
5 | using webapi.example;
6 | using webapi.Entities;
7 |
8 | namespace webapi.AutoFac
9 | {
10 | public static class ContainerBuilerCommon
11 | {
12 | public static IContainer GetWebApiContainer()
13 | {
14 | var builder = new ContainerBuilder();
15 | // 注册当前程序集里的所有webapi控制器
16 | builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
17 |
18 | #region 注册modules
19 | builder.RegisterModule();
20 | #endregion
21 |
22 | #region 注册组件,如果项目比较大可以从此方法里单独移出
23 | //这里写注册组件的代码
24 | builder.RegisterType();
25 | #region 测试
26 | builder.RegisterType().As();
27 | #endregion
28 | #endregion
29 |
30 | return builder.Build();
31 | }
32 | }
33 | }
--------------------------------------------------------------------------------
/webapi/AutoFac/Modules/LoggingModule.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 | using System.Reflection;
3 | using Autofac.Core;
4 | using log4net;
5 | using Module = Autofac.Module;
6 |
7 | namespace webapi.AutoFac.Modules
8 | {
9 | public class LoggingModule:Module
10 | {
11 | private static void InjectLoggerProperties(object instance)
12 | {
13 | var instanceType = instance.GetType();
14 |
15 | // Get all the injectable properties to set.
16 | // If you wanted to ensure the properties were only UNSET properties,
17 | // here's where you'd do it.
18 | var properties = instanceType
19 | .GetProperties(BindingFlags.Public | BindingFlags.Instance)
20 | .Where(p => p.PropertyType == typeof(ILog) && p.CanWrite && p.GetIndexParameters().Length == 0);
21 |
22 | // Set the properties located.
23 | foreach (var propToSet in properties)
24 | {
25 | propToSet.SetValue(instance, LogManager.GetLogger(instanceType), null);
26 | }
27 | }
28 |
29 | private static void OnComponentPreparing(object sender, PreparingEventArgs e)
30 | {
31 | e.Parameters = e.Parameters.Union(
32 | new[]
33 | {
34 | new ResolvedParameter(
35 | (p, i) => p.ParameterType == typeof(ILog),
36 | (p, i) => LogManager.GetLogger(p.Member.DeclaringType)
37 | ),
38 | });
39 | }
40 |
41 | protected override void AttachToComponentRegistration(IComponentRegistry componentRegistry, IComponentRegistration registration)
42 | {
43 | // Handle constructor parameters.
44 | registration.Preparing += OnComponentPreparing;
45 |
46 | // Handle properties.
47 | registration.Activated += (sender, e) => InjectLoggerProperties(e.Instance);
48 | }
49 | }
50 | }
--------------------------------------------------------------------------------
/webapi/Common/EncryptHelper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Security.Cryptography;
6 | using System.Text;
7 | using System.Web;
8 |
9 | namespace webapi.Common
10 | {
11 | public static class EncryptHelper
12 | {
13 | #region MD5加密
14 | public static string MD5Encrypt(string content)
15 | {
16 | if (content == null) return null;
17 | MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();
18 | var enStr = BitConverter.ToString(md5.ComputeHash(UTF8Encoding.UTF8.GetBytes(content))).Replace("-", "");
19 | md5.Dispose();
20 | return enStr;
21 | }
22 | #endregion
23 |
24 | #region DES加密 解密
25 | public static string DESEncrypt(string content, string key)
26 | {
27 |
28 | var rgbkey = UTF8Encoding.UTF8.GetBytes((key + "00000000").Substring(0, 8));///des加密的key必须为8位,不然会报错,以后再研究des算法的细节
29 | using (DESCryptoServiceProvider des = new DESCryptoServiceProvider())
30 | using (MemoryStream ms = new MemoryStream())
31 | using (CryptoStream encStream = new CryptoStream(ms, des.CreateEncryptor(rgbkey, rgbkey), CryptoStreamMode.Write))
32 | {
33 | encStream.Write(UTF8Encoding.UTF8.GetBytes(content), 0, UTF8Encoding.UTF8.GetBytes(content).Length);
34 | encStream.FlushFinalBlock();
35 | var enByte = ms.ToArray();
36 | var enBase64Str = Convert.ToBase64String(enByte);
37 | return enBase64Str;
38 | }
39 | }
40 |
41 | public static string DESDecrypt(string content, string key)
42 | {
43 | var rgbkey = UTF8Encoding.UTF8.GetBytes((key + "00000000").Substring(0, 8));///des加密的key必须为8位,不然会报错,以后再研究des算法的细节
44 | using (DESCryptoServiceProvider des = new DESCryptoServiceProvider())
45 | using (MemoryStream ms = new MemoryStream())
46 | using (CryptoStream encStream = new CryptoStream(ms, des.CreateDecryptor(rgbkey, rgbkey), CryptoStreamMode.Write))
47 | {
48 | var deByte = Convert.FromBase64String(content);
49 | encStream.Write(deByte, 0, deByte.Length);
50 | encStream.FlushFinalBlock();
51 | var deStr = UTF8Encoding.UTF8.GetString(ms.ToArray());
52 | return deStr;
53 | }
54 | }
55 | #endregion
56 | }
57 | }
--------------------------------------------------------------------------------
/webapi/Common/Enums.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Web;
5 |
6 | namespace webapi.Common
7 | {
8 | ///
9 | /// 数据删除标志
10 | ///
11 | public enum DeleteMark
12 | {
13 | Deleted = -1,
14 | NotDeleted = 0
15 | }
16 | ///
17 | /// 性别约定值
18 | ///
19 | public enum Gender
20 | {
21 | Unknow = 0,
22 | Male = 1,
23 | Female = 2
24 | }
25 |
26 | ///
27 | /// 用户类型
28 | ///
29 | public enum UserType
30 | {
31 | ///
32 | /// 超级管理员
33 | ///
34 | SuperAdmin,
35 | ///
36 | /// 管理员
37 | ///
38 | Admin,
39 | ///
40 | /// 普通用户
41 | ///
42 | User
43 | }
44 |
45 | public enum ResourceCategory
46 | {
47 | WebApi,
48 | Menu
49 | }
50 | }
--------------------------------------------------------------------------------
/webapi/Common/IdManager.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using webapi.Entities;
4 |
5 | namespace webapi.Common
6 | {
7 | public class IdManager
8 | {
9 | private static string _lastUserId;
10 | private static string _lastRoleId;
11 | private static string _lastResourceId;
12 | private static string _lastPermissionId;
13 | private static string _lastURMId;
14 |
15 | public static string NewUserId()
16 | {
17 | if (string.IsNullOrEmpty(_lastUserId))
18 | {
19 | using (DB db=new DB())
20 | {
21 | var userId = db.Users.OrderByDescending(a => a.Id).FirstOrDefault()?.Id;
22 | if (string.IsNullOrEmpty(userId))
23 | {
24 | _lastUserId = "1001";
25 | }
26 | }
27 | }
28 | _lastUserId = (int.Parse(_lastUserId) + 1).ToString();
29 | return _lastUserId;
30 | }
31 |
32 | public static string NewRoleId()
33 | {
34 | if (string.IsNullOrEmpty(_lastRoleId))
35 | {
36 | using (DB db = new DB())
37 | {
38 | var roleId = db.Roles.OrderByDescending(a => a.Id).FirstOrDefault()?.Id;
39 | if (string.IsNullOrEmpty(roleId))
40 | {
41 | _lastRoleId = "1";
42 | }
43 | }
44 | }
45 | _lastRoleId = (int.Parse(_lastRoleId) + 1).ToString();
46 | return _lastRoleId;
47 | }
48 |
49 | public static string NewResourceId()
50 | {
51 | if (string.IsNullOrEmpty(_lastResourceId))
52 | {
53 | using (DB db = new DB())
54 | {
55 | var ResourceId = db.Resources.OrderByDescending(a => a.Id).FirstOrDefault()?.Id;
56 | if (string.IsNullOrEmpty(ResourceId))
57 | {
58 | _lastResourceId = "1";
59 | }
60 | }
61 | }
62 | _lastResourceId = (int.Parse(_lastResourceId) + 1).ToString();
63 | return _lastResourceId;
64 | }
65 |
66 | public static string NewPermissionId()
67 | {
68 | if (string.IsNullOrEmpty(_lastPermissionId))
69 | {
70 | using (DB db = new DB())
71 | {
72 | var PermissionId = db.Permissions.OrderByDescending(a => a.Id).FirstOrDefault()?.Id;
73 | if (string.IsNullOrEmpty(PermissionId))
74 | {
75 | _lastPermissionId = "1";
76 | }
77 | }
78 | }
79 | _lastPermissionId = (int.Parse(_lastPermissionId) + 1).ToString();
80 | return _lastPermissionId;
81 | }
82 |
83 | public static string NewURMId()
84 | {
85 | if (string.IsNullOrEmpty(_lastURMId))
86 | {
87 | using (DB db = new DB())
88 | {
89 | var URMId = db.URMs.OrderByDescending(a => a.Id).FirstOrDefault()?.Id;
90 | if (string.IsNullOrEmpty(URMId))
91 | {
92 | _lastURMId = "1";
93 | }
94 | }
95 | }
96 | _lastURMId = (int.Parse(_lastURMId) + 1).ToString();
97 | return _lastURMId;
98 | }
99 | }
100 | }
--------------------------------------------------------------------------------
/webapi/Common/JWTHelper.cs:
--------------------------------------------------------------------------------
1 | using JWT;
2 | using JWT.Algorithms;
3 | using JWT.Serializers;
4 | using System;
5 | using System.Collections.Generic;
6 |
7 | namespace webapi.Common
8 | {
9 | public class JWTHelper
10 | {
11 | private IJsonSerializer _jsonSerializer;
12 | private IDateTimeProvider _dateTimeProvider;
13 | private IJwtValidator _jwtValidator;
14 | private IBase64UrlEncoder _base64UrlEncoder;
15 | private IJwtAlgorithm _jwtAlgorithm;
16 | private IJwtDecoder _jwtDecoder;
17 | private IJwtEncoder _jwtEncoder;
18 | public JWTHelper()
19 | {
20 | //非fluent写法
21 | this._jsonSerializer = new JsonNetSerializer();
22 | this._dateTimeProvider = new UtcDateTimeProvider();
23 | this._jwtValidator = new JwtValidator(_jsonSerializer, _dateTimeProvider);
24 | this._base64UrlEncoder = new JwtBase64UrlEncoder();
25 | this._jwtAlgorithm = new HMACSHA256Algorithm();
26 | this._jwtDecoder = new JwtDecoder(_jsonSerializer, _jwtValidator, _base64UrlEncoder);
27 | this._jwtEncoder = new JwtEncoder(_jwtAlgorithm, _jsonSerializer, _base64UrlEncoder);
28 |
29 |
30 | }
31 | public string Decode(string token, string key, out bool isValid, out string errMsg)
32 | {
33 | isValid = false;
34 | var result = string.Empty;
35 | try
36 | {
37 | result = _jwtDecoder.Decode(token, key, true);
38 | isValid = true;
39 | errMsg = "正确的token";
40 | return result;
41 | }
42 | catch (TokenExpiredException)
43 | {
44 | errMsg = "token过期";
45 | return result;
46 | }
47 | catch (SignatureVerificationException)
48 | {
49 | errMsg = "签名无效";
50 | return result;
51 | }
52 | catch (Exception)
53 | {
54 | errMsg = "token无效";
55 | return result;
56 | }
57 | }
58 |
59 | public T DecodeToObject(string token, string key, out bool isValid, out string errMsg)
60 | {
61 | isValid = false;
62 | try
63 | {
64 | var result = _jwtDecoder.DecodeToObject(token, key, true);
65 | isValid = true;
66 | errMsg = "正确的token";
67 | return result;
68 | }
69 | catch (TokenExpiredException)
70 | {
71 | errMsg = "token过期";
72 | return default(T);
73 | }
74 | catch (SignatureVerificationException)
75 | {
76 | errMsg = "签名无效";
77 | return default(T);
78 | }
79 | catch (Exception)
80 | {
81 | errMsg = "token无效";
82 | return default(T);
83 | }
84 | }
85 |
86 | public IDictionary DecodeToObject(string token, string key, out bool isValid, out string errMsg)
87 | {
88 | isValid = false;
89 | try
90 | {
91 | var result = _jwtDecoder.DecodeToObject(token, key, true);
92 | isValid = true;
93 | errMsg = "正确的token";
94 | return result;
95 | }
96 | catch (TokenExpiredException)
97 | {
98 | errMsg = "token过期";
99 | return null;
100 | }
101 | catch (SignatureVerificationException)
102 | {
103 | errMsg = "签名无效";
104 | return null;
105 | }
106 | catch (Exception)
107 | {
108 | errMsg = "token无效";
109 | return null;
110 | }
111 | }
112 |
113 | #region 解密
114 | public string Encode(Dictionary payload, string key, int expiredMinute = 30)
115 | {
116 | if (!payload.ContainsKey("exp"))
117 | {
118 | var exp = Math.Round((_dateTimeProvider.GetNow().AddMinutes(expiredMinute) - new DateTime(1970, 1, 1)).TotalSeconds);
119 | payload.Add("exp", exp);
120 | }
121 | return _jwtEncoder.Encode(payload, key);
122 | }
123 | #endregion
124 |
125 | }
126 | }
127 |
--------------------------------------------------------------------------------
/webapi/Configs/Config.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Web;
5 |
6 | namespace webapi.Configs
7 | {
8 | public class Config
9 | {
10 | public const string JWTKey = "shengyu";
11 | public const string SysUser = "sys";
12 | }
13 | }
--------------------------------------------------------------------------------
/webapi/Configs/WebApiConfig.cs:
--------------------------------------------------------------------------------
1 | using System.Web.Http;
2 | using webapi.Exceptions;
3 | using webapi.Security;
4 |
5 | namespace webapi.Configs
6 | {
7 | ///
8 | /// webapi 配置类
9 | ///
10 | public static class WebApiConfig
11 | {
12 | ///
13 | /// 做为委托提供给System.Web.Http.GlobalConfiguration.Configuration()
14 | /// 用于webapi以iis为服务器的情况
15 | ///
16 | ///
17 | public static void Register(HttpConfiguration config)
18 | {
19 | // Web API 配置和服务
20 | // Web API 路由
21 | config.MapHttpAttributeRoutes();
22 | config.Routes.MapHttpRoute(
23 | name: "DefaultApi",
24 | routeTemplate: "api/{controller}/{id}",
25 | defaults: new { id = RouteParameter.Optional }
26 | );
27 | }
28 | ///
29 | /// 返回webapi的httpconfiguration配置
30 | /// 用于webapi应用于owin技术时使用
31 | ///
32 | ///
33 | public static HttpConfiguration OwinWebApiConfiguration(HttpConfiguration config)
34 | {
35 | config.MapHttpAttributeRoutes();//开启属性路由
36 | config.Routes.MapHttpRoute(
37 | name: "DefaultApi",
38 | routeTemplate: "api/{controller}/{id}",
39 | defaults: new { id = RouteParameter.Optional }
40 | );
41 | config.Filters.Add(new WebApiExceptionFilterAttribute());
42 | config.Filters.Add(new IdentityBasicAuthentication());
43 | return config;
44 | }
45 | }
46 | }
--------------------------------------------------------------------------------
/webapi/ConnectionStrings.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/webapi/Entities/BaseEntity.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.ComponentModel;
3 | using System.ComponentModel.DataAnnotations;
4 | using System.ComponentModel.DataAnnotations.Schema;
5 | namespace webapi.Entities
6 | {
7 | public class BaseEntity
8 | {
9 |
10 | ///
11 | /// 创建时间
12 | ///
13 | [Column(TypeName = "datetime")]
14 | public DateTime? CreateTime { set; get; }
15 | ///
16 | /// 创建人
17 | ///
18 | [Column(TypeName = "varchar"), MaxLength(50)]
19 | public string CreateUser { set; get; }
20 | ///
21 | /// 是否删除
22 | ///
23 | [Column(TypeName = "int"), DefaultValue(webapi.Common.DeleteMark.NotDeleted)]
24 | public int DeleteMark { get; set; }
25 | }
26 | }
--------------------------------------------------------------------------------
/webapi/Entities/DB.cs:
--------------------------------------------------------------------------------
1 | using System.Data.Entity;
2 |
3 | namespace webapi.Entities
4 | {
5 | public class DB : DbContext
6 | {
7 | ///
8 | /// name=DBConnection,DBConnection为数据库连接的名字,即web.config配置文件节点connectionStrings,name值为DBConnection的数据库连接字符串
9 | ///
10 | public DB()
11 | : base("name=DBConnection")
12 | {
13 | // 默认策略为CreateDatabaseIfNotExists,即如果数据库不存在则创建,用migrations时改成MigrateDatabaseToLatestVersion,即每次第一次访问数据库时同步最新的数据库结构
14 | Database.SetInitializer(new MigrateDatabaseToLatestVersion("DBConnection"));
15 | }
16 |
17 |
18 | #region 配置所有的数据库表
19 |
20 | public DbSet Users { set; get; }
21 | public DbSet Roles { set; get; }
22 | public DbSet Resources { set; get; }
23 | public DbSet URMs { set; get; }
24 | public DbSet Permissions { set; get; }
25 | public DbSet TestTables { set; get; }
26 | #endregion
27 |
28 | }
29 | }
--------------------------------------------------------------------------------
/webapi/Entities/Permission.cs:
--------------------------------------------------------------------------------
1 | using System.ComponentModel.DataAnnotations;
2 | using System.ComponentModel.DataAnnotations.Schema;
3 |
4 | namespace webapi.Entities
5 | {
6 | ///
7 | /// 权限表,记录角色和资源的对应关系
8 | ///
9 | [Table("Permission")]
10 | public class Permission:BaseEntity
11 | {
12 | ///
13 | /// 主键
14 | ///
15 | [Key, Column(TypeName = "varchar"), MaxLength(50)]
16 | public string Id { set; get; }
17 | ///
18 | /// 资源类型,如webapi接口,菜单等
19 | ///
20 | [Column(TypeName = "varchar"), MaxLength(50)]
21 | public string RoleId { set; get; }
22 | ///
23 | /// 资源名,如“ControllerName.ActionName”,或是“url地址”
24 | ///
25 | [Column(TypeName = "varchar"), MaxLength(50)]
26 | public string ResourceId { set; get; }
27 | }
28 | }
--------------------------------------------------------------------------------
/webapi/Entities/Resource.cs:
--------------------------------------------------------------------------------
1 | using System.ComponentModel.DataAnnotations;
2 | using System.ComponentModel.DataAnnotations.Schema;
3 |
4 | namespace webapi.Entities
5 | {
6 | ///
7 | /// 需要做权限控制的资源
8 | ///
9 | [Table("Resource")]
10 | public class Resource:BaseEntity
11 | {
12 | ///
13 | /// 主键
14 | ///
15 | [Key,Column(TypeName = "varchar"),MaxLength(50)]
16 | public string Id { set; get; }
17 | ///
18 | /// 资源类型,如webapi接口,菜单等
19 | ///
20 | [Column(TypeName = "varchar"), MaxLength(20)]
21 | public string Category { set; get; }
22 | ///
23 | /// 资源名(唯一性),如“ControllerName.ActionName”,或是“url地址”
24 | ///
25 | [Column(TypeName = "varchar"), MaxLength(100)]
26 | public string Name { set; get; }
27 | ///
28 | /// 资源描述
29 | ///
30 | [Column(TypeName = "varchar"), MaxLength(200)]
31 | public string Description { set; get; }
32 | }
33 | }
--------------------------------------------------------------------------------
/webapi/Entities/Role.cs:
--------------------------------------------------------------------------------
1 | using System.ComponentModel.DataAnnotations;
2 | using System.ComponentModel.DataAnnotations.Schema;
3 |
4 | namespace webapi.Entities
5 | {
6 | ///
7 | /// 角色表
8 | ///
9 | [Table("Role")]
10 | public partial class Role:BaseEntity
11 | {
12 | ///
13 | /// 角色ID
14 | ///
15 | [Key, Column(TypeName = "varchar"), MaxLength(50)]
16 | public string Id { get; set; }
17 | ///
18 | /// 角色名
19 | ///
20 | [Column(TypeName = "varchar"), MaxLength(20)]
21 | public string Name { get; set; }
22 |
23 | }
24 | }
--------------------------------------------------------------------------------
/webapi/Entities/TestTable.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.ComponentModel.DataAnnotations;
3 | using System.ComponentModel.DataAnnotations.Schema;
4 |
5 | namespace webapi.Entities
6 | {
7 | [Table("Test")]
8 | public class TestTable
9 | {
10 | [Key,Column(TypeName = "varchar"),MaxLength(50)]
11 | public string Id { set; get; }
12 | [Column(TypeName = "int")]
13 | public int? Age { set; get; }
14 | [Column(TypeName = "datetime")]
15 | public DateTime? CreateDateTime { get; set; }
16 | [Column(TypeName = "varchar"), MaxLength(20)]
17 | public string AddColumn1 { set; get; }
18 |
19 | [Column(TypeName = "varchar"), MaxLength(20)]
20 | public string AddColumn2 { set; get; }
21 | }
22 | }
--------------------------------------------------------------------------------
/webapi/Entities/URM.cs:
--------------------------------------------------------------------------------
1 | using System.ComponentModel.DataAnnotations;
2 | using System.ComponentModel.DataAnnotations.Schema;
3 |
4 | namespace webapi.Entities
5 | {
6 | ///
7 | /// 用户角色关系对应表,user role map
8 | ///
9 | [Table("URM")]
10 | public partial class URM:BaseEntity
11 | {
12 | ///
13 | /// ID主键
14 | ///
15 | [Key, Column(TypeName = "varchar"), MaxLength(50)]
16 | public string Id { get; set; }
17 | ///
18 | /// 用户ID
19 | ///
20 | [Column(TypeName = "varchar"), MaxLength(50)]
21 | public string UserId { get; set; }
22 | ///
23 | /// 角色ID
24 | ///
25 | [Column(TypeName = "varchar"), MaxLength(50)]
26 | public string RoleId { get; set; }
27 |
28 | }
29 | }
--------------------------------------------------------------------------------
/webapi/Entities/User.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.ComponentModel.DataAnnotations;
3 | using System.ComponentModel.DataAnnotations.Schema;
4 |
5 | namespace webapi.Entities
6 | {
7 | ///
8 | /// 用户表
9 | ///
10 | [Table("User")]
11 | public partial class User:BaseEntity
12 | {
13 | ///
14 | /// 主键
15 | ///
16 | [Key, Column(TypeName = "varchar"), MaxLength(50)]
17 | public string Id { get; set; }
18 | ///
19 | /// 登录名
20 | ///
21 | [Column(TypeName = "varchar"), MaxLength(50)]
22 | public string LoginName { get; set; }
23 | ///
24 | /// 真实姓名
25 | ///
26 | [Column(TypeName = "varchar"), MaxLength(50)]
27 | public string Name { get; set; }
28 | ///
29 | /// 密码,用于存储密码的md5加密
30 | ///
31 | [Column(TypeName = "varchar"), MaxLength(50)]
32 | public string Pwd { get; set; }
33 | ///
34 | /// 性别,1男2女,对应Gender枚举
35 | ///
36 | [Column(TypeName = "int")]
37 | public int? Gender { get; set; }
38 | ///
39 | /// 身份证
40 | ///
41 | [Column(TypeName = "varchar"), MaxLength(50)]
42 | public string IdentityCard { get; set; }
43 | ///
44 | /// 电话
45 | ///
46 | [Column(TypeName = "varchar"), MaxLength(50)]
47 | public string Tel { get; set; }
48 | ///
49 | /// 出生日期
50 | ///
51 | [Column(TypeName = "datetime")]
52 | public DateTime? Birthdate { get; set; }
53 | ///
54 | /// 头像
55 | ///
56 | [Column(TypeName = "varchar"), MaxLength(500)]
57 | public string UserPic { get; set; }
58 | }
59 | }
--------------------------------------------------------------------------------
/webapi/Exceptions/KnownException.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.Serialization;
3 |
4 | namespace webapi.Exceptions
5 | {
6 | public class KnownException : Exception
7 | {
8 | public KnownException():base()
9 | {
10 | }
11 |
12 | public KnownException(string message) : base(message)
13 | {
14 | }
15 |
16 | public KnownException(string message, Exception innerException) : base(message, innerException)
17 | {
18 | }
19 |
20 | protected KnownException(SerializationInfo info, StreamingContext context) : base(info, context)
21 | {
22 | }
23 | }
24 | }
--------------------------------------------------------------------------------
/webapi/Exceptions/WebApiExceptionFilterAttribute.cs:
--------------------------------------------------------------------------------
1 | using System.Net;
2 | using System.Net.Http;
3 | using System.Web.Http.Filters;
4 | using log4net;
5 |
6 | namespace webapi.Exceptions
7 | {
8 | public class WebApiExceptionFilterAttribute : ExceptionFilterAttribute
9 | {
10 | public override void OnException(HttpActionExecutedContext actionExecutedContext)
11 | {
12 | var exception = actionExecutedContext.Exception;//获取产生的异常对象
13 | var exceptionMessage = exception.Message;
14 | var logMessage =
15 | $@"controller.action={actionExecutedContext.ActionContext.ControllerContext.ControllerDescriptor.ControllerName}.{actionExecutedContext.ActionContext.ActionDescriptor.ActionName}:exception="
16 | + exception.Message;//异常内容
17 | ILog log = LogManager.GetLogger(actionExecutedContext.ActionContext.ControllerContext.Controller.GetType());
18 | if (exception is KnownException)
19 | {
20 | log.Debug(logMessage);
21 | }
22 | else
23 | {
24 | log.Error(logMessage, exception);
25 | }
26 | actionExecutedContext.Response = actionExecutedContext.Request.CreateErrorResponse(HttpStatusCode.BadRequest, exceptionMessage);
27 | }
28 |
29 | }
30 | }
--------------------------------------------------------------------------------
/webapi/Global.asax:
--------------------------------------------------------------------------------
1 | <%@ Application Codebehind="Global.asax.cs" Inherits="webapi.Global" Language="C#" %>
2 |
--------------------------------------------------------------------------------
/webapi/Global.asax.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Web.Http;
3 | using webapi.Configs;
4 |
5 |
6 | namespace webapi
7 | {
8 | ///
9 | /// 部署在iis里时,iis的入口为HttpApplication.Application_Start函数,所以webapi要在此函数里做配置。
10 | /// 如果用owin通道(iis的asp.net pipeline 将不再启用),则不需要Global.asax文件
11 | /// 在部署时如果是用owin技术,Global.asax不用删除,请求是不会进过这里的,除非删除了bin目录下的Microsoft.Owin.Host.SystemWeb.dll
12 | ///
13 | public class Global : System.Web.HttpApplication
14 | {
15 | protected void Application_Start(object sender, EventArgs e)
16 | {
17 | #region webapi的相关配置
18 | // GlobalConfiguration类在Microsoft.AspNet.WebApi.Core里,用nuget添加Microsoft.AspNet.WebApi
19 | //GlobalConfiguration类在Microsoft.AspNet.WebApi.WebHost里有也定义
20 | GlobalConfiguration.Configure(WebApiConfig.Register);
21 |
22 | #endregion
23 |
24 | }
25 |
26 | protected void Session_Start(object sender, EventArgs e)
27 | {
28 |
29 | }
30 |
31 | protected void Application_BeginRequest(object sender, EventArgs e)
32 | {
33 |
34 | }
35 |
36 | protected void Application_AuthenticateRequest(object sender, EventArgs e)
37 | {
38 |
39 | }
40 |
41 | protected void Application_Error(object sender, EventArgs e)
42 | {
43 |
44 | }
45 |
46 | protected void Session_End(object sender, EventArgs e)
47 | {
48 |
49 | }
50 |
51 | protected void Application_End(object sender, EventArgs e)
52 | {
53 |
54 | }
55 | }
56 | }
--------------------------------------------------------------------------------
/webapi/Log4net.config:
--------------------------------------------------------------------------------
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 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/webapi/Microsoft.Owin.Host.HttpListener.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shengyu-kmust/webapi/35c9cb30ded1e3d232619e314237b57f35b55dce/webapi/Microsoft.Owin.Host.HttpListener.dll
--------------------------------------------------------------------------------
/webapi/Microsoft.Owin.Hosting.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shengyu-kmust/webapi/35c9cb30ded1e3d232619e314237b57f35b55dce/webapi/Microsoft.Owin.Hosting.dll
--------------------------------------------------------------------------------
/webapi/Microsoft.Owin.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shengyu-kmust/webapi/35c9cb30ded1e3d232619e314237b57f35b55dce/webapi/Microsoft.Owin.dll
--------------------------------------------------------------------------------
/webapi/Migrations/Configuration.cs:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shengyu-kmust/webapi/35c9cb30ded1e3d232619e314237b57f35b55dce/webapi/Migrations/Configuration.cs
--------------------------------------------------------------------------------
/webapi/Owin.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shengyu-kmust/webapi/35c9cb30ded1e3d232619e314237b57f35b55dce/webapi/Owin.dll
--------------------------------------------------------------------------------
/webapi/Owin/Startup.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Owin;
2 | using Owin;
3 | using System.Threading.Tasks;
4 | using System.Web.Http;
5 | using Autofac.Integration.WebApi;
6 | using webapi.AutoFac;
7 | using webapi.Configs;
8 |
9 | // 标识webapiOwin.Startup类为owin的启动类,也可写在AssemblyInfo.cs文件里
10 | [assembly: OwinStartup(typeof(webapi.Owin.Startup))]
11 |
12 | namespace webapi.Owin
13 | {
14 | public class Startup
15 | {
16 | ///
17 | /// owin的http请求管道配置函数
18 | ///
19 | ///
20 | public void Configuration(IAppBuilder app)
21 | {
22 | // 有关如何配置应用程序的详细信息,请访问 https://go.microsoft.com/fwlink/?LinkID=316888
23 |
24 | #region 测试用
25 | //app.Run(context =>
26 | //{
27 | // context.Response.Write("这是owin管道");
28 | // return Task.FromResult(0);
29 | //});
30 | #endregion
31 |
32 | #region 写在前面的配置
33 | // 获取webapi的配置
34 | var config = WebApiConfig.OwinWebApiConfiguration(new HttpConfiguration());
35 | // 获取webapi的依赖注入容器
36 | var container = ContainerBuilerCommon.GetWebApiContainer();
37 | // 配置webapi的依赖注入
38 | config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
39 | #endregion
40 |
41 | #region owin组件注册(要注意顺序)
42 |
43 | app.UseAutofacMiddleware(container);// 先注册autofac组件,需要依赖注入功能的组件在此后注册
44 | app.UseAutofacWebApi(config);//注册AutofacWebApi组件后再注册WebApi组件
45 | app.UseWebApi(config);
46 | #endregion
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/webapi/OwinHost.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shengyu-kmust/webapi/35c9cb30ded1e3d232619e314237b57f35b55dce/webapi/OwinHost.exe
--------------------------------------------------------------------------------
/webapi/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 | using log4net.Config;
5 |
6 | // 有关程序集的常规信息通过下列特性集
7 | // 控制。更改这些特性值可修改
8 | // 与程序集关联的信息。
9 | [assembly: AssemblyTitle("webapi")]
10 | [assembly: AssemblyDescription("")]
11 | [assembly: AssemblyConfiguration("")]
12 | [assembly: AssemblyCompany("")]
13 | [assembly: AssemblyProduct("webapi")]
14 | [assembly: AssemblyCopyright("Copyright © 2018")]
15 | [assembly: AssemblyTrademark("")]
16 | [assembly: AssemblyCulture("")]
17 |
18 | // 将 ComVisible 设置为 false 会使此程序集中的类型
19 | // 对 COM 组件不可见。如果需要
20 | // 从 COM 访问此程序集中的某个类型,请针对该类型将 ComVisible 特性设置为 true。
21 | [assembly: ComVisible(false)]
22 |
23 | // 如果此项目向 COM 公开,则下列 GUID 用于 typelib 的 ID
24 | [assembly: Guid("7a63dca6-5f3f-440a-934e-a92c162c0e82")]
25 |
26 | // 程序集的版本信息由下列四个值组成:
27 | //
28 | // 主版本
29 | // 次版本
30 | // 内部版本号
31 | // 修订版本
32 | //
33 | // 可以指定所有值,也可以使用“修订号”和“内部版本号”的默认值,
34 | // 方法是按如下所示使用 "*":
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 | // log4net的配置文件,参考:http://logging.apache.org/log4net/release/manual/configuration.html
38 | [assembly: XmlConfigurator(Watch = true, ConfigFile = "Log4Net.config")]
--------------------------------------------------------------------------------
/webapi/Security/IdentityBasicAuthentication.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 | using System.Net;
3 | using System.Net.Http;
4 | using System.Security.Claims;
5 | using System.Threading;
6 | using System.Threading.Tasks;
7 | using System.Web.Http.Filters;
8 | using System.Web.Http.Results;
9 | using webapi.Common;
10 | using webapi.Configs;
11 |
12 | namespace webapi.Security
13 | {
14 | public class IdentityBasicAuthentication:IAuthenticationFilter
15 | {
16 | public bool AllowMultiple { get; }
17 | ///
18 | /// 请求先经过AuthenticateAsync
19 | ///
20 | ///
21 | ///
22 | ///
23 | public Task AuthenticateAsync(HttpAuthenticationContext context, CancellationToken cancellationToken)
24 | {
25 | // 1、获取token
26 | context.Request.Headers.TryGetValues("token", out var tokenHeaders);
27 | // 2、如果没有token,不做任何处理
28 | if (tokenHeaders == null || !tokenHeaders.Any())
29 | {
30 | return Task.FromResult(0);
31 | }
32 | // 3、如果token验证通过,则写入到identity,如果未通过则设置错误
33 | var jwtHelper=new JWTHelper();
34 | var payLoadClaims=jwtHelper.DecodeToObject(tokenHeaders.FirstOrDefault(),Config.JWTKey, out bool isValid, out string errMsg);
35 | if (isValid)
36 | {
37 | var identity = new ClaimsIdentity("jwt", "user", "role");//只要ClaimsIdentity设置了authenticationType,authenticated就为true,后面的authority根据authenticated=true来做权限
38 | foreach (var keyValuePair in payLoadClaims)
39 | {
40 | // 一个用户可以拥有多种角色
41 | if (keyValuePair.Key=="role")
42 | {
43 | foreach (var roleItem in keyValuePair.Value.ToString().Split(','))
44 | {
45 | identity.AddClaim(new Claim("role", roleItem));
46 | }
47 | }
48 | else
49 | {
50 | identity.AddClaim(new Claim(keyValuePair.Key, keyValuePair.Value.ToString()));
51 | }
52 | }
53 | // 最好是http上下文的principal和进程的currentPrincipal都设置
54 | context.Principal = new ClaimsPrincipal(identity);
55 | Thread.CurrentPrincipal = new ClaimsPrincipal(identity);
56 | }
57 | else
58 | {
59 | context.ErrorResult = new ResponseMessageResult(new HttpResponseMessage()
60 | {
61 | StatusCode = HttpStatusCode.ProxyAuthenticationRequired,
62 | Content = new StringContent(errMsg)
63 | });
64 | }
65 | return Task.FromResult(0);
66 | }
67 |
68 | ///
69 | /// 请求后经过AuthenticateAsync
70 | ///
71 | ///
72 | ///
73 | ///
74 | public Task ChallengeAsync(HttpAuthenticationChallengeContext context, CancellationToken cancellationToken)
75 | {
76 | return Task.FromResult(0);
77 | }
78 | }
79 | }
--------------------------------------------------------------------------------
/webapi/Security/RBAuthorizeAttribute.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Net;
5 | using System.Net.Http;
6 | using System.Security.Principal;
7 | using System.Web.Http;
8 | using System.Web.Http.Controllers;
9 | using webapi.Entities;
10 | using webapi.Services;
11 |
12 | namespace webapi.Security
13 | {
14 | ///
15 | /// Role Basic AuthorizeAttribute(基于角色的授权)
16 | ///
17 | public class RBAuthorizeAttribute : AuthorizeAttribute
18 | {
19 | public string Description { set; get; }
20 | protected override bool IsAuthorized(HttpActionContext actionContext)
21 | {
22 | // 这是base.IsAuthorized里的逻辑
23 | //IPrincipal principal = actionContext.ControllerContext.RequestContext.Principal;
24 | //return principal != null && principal.Identity != null
25 | // && principal.Identity.IsAuthenticated &&
26 | // (
27 | // (this._usersSplit.Length <= 0 || ((IEnumerable)this._usersSplit).Contains(principal.Identity.Name, (IEqualityComparer)StringComparer.OrdinalIgnoreCase))
28 | // &&
29 | // (this._rolesSplit.Length <= 0 || ((IEnumerable)this._rolesSplit).Any(new Func(principal.IsInRole)))
30 | // );
31 |
32 | // 下在可替换成自己的授权逻辑代码
33 | AuthorizeService authorizeService =new AuthorizeService(new DB());
34 | var resourceName = actionContext.ActionDescriptor.GetCustomAttributes().Any()
35 | ? actionContext.ActionDescriptor.ActionName
36 | : actionContext.ControllerContext.ControllerDescriptor.ControllerName;
37 | var roleNames = authorizeService.GetResourceRoleNames(resourceName);
38 | IPrincipal principal = actionContext.ControllerContext.RequestContext.Principal;
39 | return principal != null && principal.Identity != null
40 | && principal.Identity.IsAuthenticated &&
41 | (
42 | (((IEnumerable)roleNames).Any(new Func(principal.IsInRole)))
43 | );
44 | }
45 |
46 | protected override void HandleUnauthorizedRequest(HttpActionContext actionContext)
47 | {
48 | actionContext.Response =
49 | actionContext.ControllerContext.Request.CreateErrorResponse(HttpStatusCode.Unauthorized, "未授权");
50 | }
51 | }
52 | }
--------------------------------------------------------------------------------
/webapi/Services/AuthorizeService.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Web;
5 | using webapi.Entities;
6 |
7 |
8 | namespace webapi.Services
9 | {
10 | public class AuthorizeService:BaseService
11 | {
12 | public AuthorizeService(DB db) : base(db)
13 | {
14 | }
15 | ///
16 | /// 获取资源的角色名数组
17 | ///
18 | ///
19 | ///
20 | public string[] GetResourceRoleNames(string resourceName)
21 | {
22 | var resource=_db.Resources.FirstOrDefault(a => a.Name == resourceName);
23 | var roleIds = _db.Permissions.Where(a => a.ResourceId == resource.Id).Select(a => a.RoleId).ToArray();
24 | var roleNames = _db.Roles.Where(a => roleIds.Contains(a.Id)).Select(a => a.Name).ToArray();
25 | return roleNames;
26 | }
27 | ///
28 | /// 获取所有资源的角色
29 | ///
30 | ///
31 | public List<(string resourceName, string roleNames)> GetAllResourceRoleNames()
32 | {
33 | var result=new List<(string resourceName, string roleNames)>();
34 | var query = from a in _db.Permissions
35 | join b in _db.Resources on a.ResourceId equals b.Id
36 | join c in _db.Roles on a.RoleId equals c.Id
37 | group new{a,b,c} by b.Name into g
38 | select new
39 | {
40 | resourceName=g.Key,
41 | roleNames=g.Select(i => i.c.Name)
42 | };
43 |
44 | var list = query.ToList().Select(a => new
45 | {
46 | a.resourceName,
47 | roleNames=string.Join(",", a.roleNames.ToArray().Distinct())
48 | });
49 | foreach (var x1 in list)
50 | {
51 | result.Add((x1.resourceName,x1.roleNames));
52 | }
53 | return result;
54 | }
55 | }
56 | }
--------------------------------------------------------------------------------
/webapi/Services/BaseService.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Web;
5 | using webapi.Entities;
6 |
7 | namespace webapi.Services
8 | {
9 | public class BaseService:IDisposable
10 | {
11 | protected DB _db;
12 |
13 | public BaseService(DB db)
14 | {
15 | _db = db;
16 | }
17 |
18 | #region IDisposable Support
19 | private bool disposedValue = false; // 要检测冗余调用
20 |
21 | protected virtual void Dispose(bool disposing)
22 | {
23 | if (!disposedValue)
24 | {
25 | if (disposing)
26 | {
27 | // TODO: 释放托管状态(托管对象)。
28 | _db?.Dispose();
29 | }
30 | // TODO: 释放未托管的资源(未托管的对象)并在以下内容中替代终结器。
31 | // TODO: 将大型字段设置为 null。
32 |
33 | disposedValue = true;
34 | }
35 | }
36 |
37 | // TODO: 仅当以上 Dispose(bool disposing) 拥有用于释放未托管资源的代码时才替代终结器。
38 | // ~BaseService() {
39 | // // 请勿更改此代码。将清理代码放入以上 Dispose(bool disposing) 中。
40 | // Dispose(false);
41 | // }
42 |
43 | // 添加此代码以正确实现可处置模式。
44 | public void Dispose()
45 | {
46 | // 请勿更改此代码。将清理代码放入以上 Dispose(bool disposing) 中。
47 | Dispose(true);
48 | // TODO: 如果在以上内容中替代了终结器,则取消注释以下行。
49 | // GC.SuppressFinalize(this);
50 | }
51 | #endregion
52 | }
53 | }
--------------------------------------------------------------------------------
/webapi/Services/PermissionService.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 | using webapi.Entities;
3 |
4 | namespace webapi.Services
5 | {
6 | public class PermissionService:BaseService
7 | {
8 | public PermissionService(DB db) : base(db)
9 | {
10 | }
11 |
12 | public string[] GetWebApiResourceRoleNames(string resourceName)
13 | {
14 | var resource = _db.Resources.FirstOrDefault(a => a.Name == resourceName);
15 | var roleIds= _db.Permissions.Where(a => a.ResourceId == resource.Id).Select(a => a.RoleId).ToArray();
16 | var roleNames = _db.Roles.Where(a => roleIds.Contains(a.Id)).Select(a => a.Name).ToArray();
17 | return roleNames;
18 | }
19 | }
20 | }
--------------------------------------------------------------------------------
/webapi/Services/ResourceService.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Data.Entity.Migrations;
4 | using System.Linq;
5 | using System.Reflection;
6 | using System.Web.Http;
7 | using webapi.Common;
8 | using webapi.Entities;
9 | using webapi.Security;
10 |
11 | namespace webapi.Services
12 | {
13 | public class ResourceService : BaseService
14 | {
15 | public ResourceService(DB db) : base(db)
16 | {
17 | }
18 |
19 | public string[] GetResourceRoles(string resourceId)
20 | {
21 | return new string[] { };
22 | }
23 |
24 | ///
25 | /// 增加或修改资源
26 | ///
27 | ///
28 | ///
29 | ///
30 | public void AddOrUpdateResource(string name, string description, string category)
31 | {
32 | var resource = _db.Resources.FirstOrDefault(a => a.Name == name);
33 | if (resource == null)
34 | {
35 | resource = new Resource()
36 | {
37 | Id = IdManager.NewResourceId(),
38 | Name = name,
39 | Description = description,
40 | Category = category,
41 | CreateTime = DateTime.Now,
42 | DeleteMark = (int)DeleteMark.NotDeleted
43 | };
44 | }
45 | else
46 | {
47 | resource.Description = string.IsNullOrEmpty(description) ? resource.Description : description;
48 | resource.Name = string.IsNullOrEmpty(name) ? resource.Name : name;
49 | resource.Category = string.IsNullOrEmpty(category) ? resource.Category : category; ;
50 | }
51 | _db.Resources.AddOrUpdate(resource);
52 | _db.SaveChanges();
53 | }
54 |
55 | ///
56 | /// 更新webapi的权限资源到数据库
57 | ///
58 | public void UpdateWebApiResource()
59 | {
60 | var resources = GetAllWebApiResource();
61 | resources.ForEach(a =>
62 | {
63 | AddOrUpdateResource(a.Name, a.Description, a.Category);
64 | });
65 | }
66 |
67 | ///
68 | /// 获取所有webapi的授权资源列表
69 | ///
70 | ///
71 | public List GetAllWebApiResource()
72 | {
73 | var allController = Assembly.GetExecutingAssembly().ExportedTypes
74 | .Where(a => a.IsSubclassOf(typeof(ApiController))).ToList();
75 | var resources = new List();
76 | allController.ForEach(controller =>
77 | {
78 | if (Attribute.IsDefined(controller, typeof(RBAuthorizeAttribute)))
79 | {
80 | var description = ((RBAuthorizeAttribute)controller.GetCustomAttribute(typeof(RBAuthorizeAttribute))).Description;
81 | resources.Add(new Resource()
82 | {
83 | Category = ResourceCategory.WebApi.ToString(),
84 | CreateUser = Configs.Config.SysUser,
85 | Description = description,
86 | Name = controller.Name
87 | });
88 | }
89 | controller.GetMethods().Where(b => Attribute.IsDefined(b, typeof(RBAuthorizeAttribute))).ToList().ForEach(
90 | action =>
91 | {
92 | var description = ((RBAuthorizeAttribute)action.GetCustomAttribute(typeof(RBAuthorizeAttribute))).Description;
93 | resources.Add(new Resource()
94 | {
95 | Category = ResourceCategory.WebApi.ToString(),
96 | CreateUser = Configs.Config.SysUser,
97 | Description = description,
98 | Name = action.Name
99 | });
100 | });
101 | });
102 | return resources;
103 | }
104 | }
105 | }
--------------------------------------------------------------------------------
/webapi/Web.Debug.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
17 |
18 |
30 |
31 |
--------------------------------------------------------------------------------
/webapi/Web.Release.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
17 |
18 |
19 |
31 |
32 |
--------------------------------------------------------------------------------
/webapi/Web.config:
--------------------------------------------------------------------------------
1 |
2 |
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 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
--------------------------------------------------------------------------------
/webapi/example/EFTestController.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 | using System.Web.Http;
3 | using webapi.Entities;
4 |
5 | namespace webapi.example
6 | {
7 | public class EFTestController : ApiController
8 | {
9 | public IHttpActionResult Get()
10 | {
11 | using (DB db=new DB())
12 | {
13 | var list = db.TestTables;
14 | return Ok(list.ToList());
15 | }
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/webapi/example/ExceptionTestController.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Web.Http;
3 | using webapi.Exceptions;
4 |
5 | namespace webapi.example
6 | {
7 | [RoutePrefix("api/exceptionTest")]
8 | public class ExceptionTestController : ApiController
9 | {
10 | ///
11 | /// 模拟程序bug抛出的异常
12 | ///
13 | ///
14 | [Route("unknown"),HttpGet]
15 | public IHttpActionResult UnKnow()
16 | {
17 | throw new Exception("未知的异常");
18 | }
19 | ///
20 | /// 模拟主动抛出的业务异常
21 | ///
22 | ///
23 | [Route("known"), HttpGet]
24 | public IHttpActionResult Know()
25 | {
26 | throw new KnownException("已知的异常");
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/webapi/example/IMyLog.cs:
--------------------------------------------------------------------------------
1 | using log4net;
2 |
3 | namespace webapi.example
4 | {
5 | public interface IMyLog:ILog
6 | {
7 | }
8 | }
--------------------------------------------------------------------------------
/webapi/example/IOCTestController.cs:
--------------------------------------------------------------------------------
1 | using System.Web.Http;
2 | ///
3 | /// 本代码用来测试依赖注入是否正常
4 | ///
5 | namespace webapi.example
6 | {
7 | public class IOCTestController : ApiController
8 | {
9 | private People _people;
10 | public IOCTestController(People people)
11 | {
12 | _people = people;
13 | }
14 |
15 | public IHttpActionResult GetLanguage()
16 | {
17 | return Ok(_people.Language());
18 | }
19 | }
20 |
21 | public interface People
22 | {
23 | string Language();
24 | }
25 |
26 | public class Chinese : People
27 | {
28 | public string Language()
29 | {
30 | return "汉语";
31 | }
32 | }
33 |
34 | public class American:People
35 | {
36 | public string Language()
37 | {
38 | return "english";
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/webapi/example/LogTestController.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Web.Http;
3 | using log4net;
4 |
5 | ///
6 | /// 日志处理测试接口,使用log4net
7 | ///
8 | namespace webapi.example
9 | {
10 | public class LogTestController : ApiController
11 | {
12 | private ILog _log;
13 |
14 | public LogTestController(ILog log)
15 | {
16 | _log = log;
17 | }
18 | public IHttpActionResult Get()
19 | {
20 | _log.Debug("测试debug",new Exception("debug异常"));
21 | _log.Info("测试Info", new Exception("Info异常"));
22 | _log.Warn("测试Warn", new Exception("Warn异常"));
23 | _log.Error("测试Error", new Exception("Error异常"));
24 | _log.Fatal("测试Fatal", new Exception("Fatal异常"));
25 | return Ok("已经写入日志");
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/webapi/example/SecurityTestController.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Net.Http;
3 | using System.Security.Claims;
4 | using System.Web.Http;
5 | using webapi.Common;
6 | using webapi.Security;
7 |
8 | namespace webapi.example
9 | {
10 | [RoutePrefix("api/security"), RBAuthorize(Roles = "role",Description = "测试权限控制器")]
11 | //[RoutePrefix("api/security"),RBAuthorize(Description = "SecurityTestController")]
12 | public class SecurityTestController : ApiController
13 | {
14 | ///
15 | /// 通过get请求里传过来的值生成token
16 | ///
17 | ///
18 | [Route("token"),HttpGet,AllowAnonymous]
19 | public IHttpActionResult GetToken()
20 | {
21 | var dic=new Dictionary();
22 | foreach (var queryNameValuePair in Request.GetQueryNameValuePairs())
23 | {
24 | dic.Add(queryNameValuePair.Key,queryNameValuePair.Value);
25 | }
26 | var token=new JWTHelper().Encode(dic, "shengyu",30);
27 | return Ok(token);
28 | }
29 |
30 | ///
31 | /// 返回token里加密的信息
32 | ///
33 | ///
34 | [Route("GetUserInfoFromToken"),HttpGet, AllowAnonymous]
35 | public IHttpActionResult GetUser()
36 | {
37 | var user = (ClaimsPrincipal)User;
38 | var dic=new Dictionary();
39 | foreach (var userClaim in user.Claims)
40 | {
41 | dic.Add(userClaim.Type,userClaim.Value);
42 | }
43 | return Ok(dic);
44 | }
45 |
46 | #region 硬编码的方式实现简单的权限控制
47 |
48 | ///
49 | /// 只有某种角色的用户才有权限访问
50 | ///
51 | ///
52 | [Route("byCode/onlyRoles"), RBAuthorize(Roles = "admin,superAdmin",Description = "OnlyRoles_SetByCode"),HttpGet]
53 | public IHttpActionResult OnlyRoles_SetByCode()
54 | {
55 | return Ok("OnlyRoles_SetByCode,仅管理员能访问");
56 | }
57 |
58 | ///
59 | /// 只有某种角色的用户才有权限访问
60 | ///
61 | ///
62 | [Route("byCode/onlyRoles2"), RBAuthorize(Roles = "user,member",Description = "OnlyRoles_SetByCode2"), HttpGet]
63 | public IHttpActionResult OnlyRoles_SetByCode2()
64 | {
65 | return Ok("OnlyRoles_SetByCode2,仅角色为user,member能访问");
66 | }
67 |
68 | ///
69 | /// 只有某几个用户才有权限访问
70 | ///
71 | ///
72 | [Route("byCode/onlyUsers"), RBAuthorize(Users = "张三,李四",Description = "OnlyUsers_SetByCode"),HttpGet]
73 | public IHttpActionResult OnlyUsers_SetByCode()
74 | {
75 | return Ok("OnlyRoles_SetByCode,仅张三和李四才能访问");
76 | }
77 | #endregion
78 |
79 | #region 配置的方式实现基于角色的权限控制
80 |
81 | [Route("dynamic/onlyRoles1"), RBAuthorize(Description = "Dynamic_OnlyRoles1"), HttpGet]
82 | public IHttpActionResult Dynamic_OnlyRoles1()
83 | {
84 | return Ok("成功访问Dynamic_OnlyRoles1");
85 | }
86 | [Route("dynamic/onlyRoles2"), RBAuthorize(Description = "Dynamic_OnlyRoles2"), HttpGet]
87 | public IHttpActionResult Dynamic_OnlyRoles2()
88 | {
89 | return Ok("成功访问Dynamic_OnlyRoles2");
90 | }
91 | [Route("dynamic/onlyRoles3"), AllowAnonymous, HttpGet]
92 | public IHttpActionResult Dynamic_OnlyRoles3()
93 | {
94 | return Ok("成功访问Dynamic_OnlyRoles3,此方法不需要权限及可访问");
95 | }
96 | #endregion
97 | }
98 |
99 | }
100 |
--------------------------------------------------------------------------------
/webapi/example/TestController.cs:
--------------------------------------------------------------------------------
1 | using System.Web.Http;
2 | using webapi.Entities;
3 | using webapi.Services;
4 |
5 | namespace webapi.example
6 | {
7 | [RoutePrefix("api/Test")]
8 | public class TestController : ApiController
9 | {
10 | [Route("get"),HttpGet]
11 | public IHttpActionResult Get()
12 | {
13 | return Ok("this is TestController.Get()");
14 | }
15 |
16 | [Route("Test"), HttpGet]
17 | public IHttpActionResult Test()
18 | {
19 | ResourceService service = new ResourceService(new DB());
20 | service.UpdateWebApiResource();
21 | return Ok("成功更新");
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/webapi/packages.config:
--------------------------------------------------------------------------------
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 |
--------------------------------------------------------------------------------
/webapi/readme.txt:
--------------------------------------------------------------------------------
1 | ===============================================================================
2 | 项目结构
3 | --Configs
4 | --Owin
5 |
6 |
7 |
8 |
9 | ===============================================================================
10 | 用到的技术
11 | Owin
12 | 实现webapi应用和服务器的解耦
13 | Autofac
14 | 依赖注入,面向接口编程
15 | Log4net
16 | 日志
17 | EF
18 | 数据库访问层,用code first
19 |
20 |
21 |
22 |
23 |
24 | ===============================================================================
25 | nuget包及dll介绍
26 | webapi相关的包
27 | --包Microsoft.AspNet.WebApi.Core
28 | packages里无dll,依赖于Microsoft.AspNet.WebApi.WebHost,
29 | webapi项目的必需包,安装后会下载Microsoft.AspNet.WebApi.WebHost和Microsoft.AspNet.WebApi.Core
30 | --包Microsoft.AspNet.WebApi.WebHost
31 | 在iis环境下用webapi时的必需包
32 | 含:System.Web.Http.WebHost.dll,依赖于包Microsoft.AspNet.WebApi.Core
33 | --包Microsoft.AspNet.WebApi.Core
34 | webapi技术的核心包
35 | 含:System.Web.Http.dll,依赖于包Microsoft.AspNet.WebApi.Client
36 | --包Microsoft.AspNet.WebApi.Client
37 | webapi核心包所依赖的类库
38 | 含:System.Net.Http.Formatting.dll
39 |
40 | owin相关
41 | --包Owin
42 | owin的接口规范
43 | --包Microsoft.Owin
44 | microsoft对owin规范的扩展
45 | --包Microsoft.Owin.Host.SystemWeb
46 | owin以iis为宿主时的必备包,负责拦截iis请求到owin管道,如果删除此dll,则会返回到iis asp.net request pipeline里
47 | 依赖:Microsoft.Owin和Owin包
48 | owin host相关
49 | --包Microsoft.Owin.SelfHost
50 | owin自宿主必备包,写自宿主程序时必需引用
51 | 依赖Microsoft.Owin.Hosting,Owin,Microsoft.Owin.Diagnostics,Microsoft.Owin.Host.HttpListener,Microsoft.Owin
52 | --包Microsoft.Owin.Hosting
53 | Provides default infrastructure types for hosting and running OWIN-based applications.
54 | --包Microsoft.Owin.Diagnostics
55 | Provides middleware components to assist in developing OWIN-based applications.
56 | --包Microsoft.Owin.Host.HttpListener
57 | OWIN server built on the .NET Framework's HttpListener class. Currently the default server used for self-hosting.
58 |
59 | webapi owin相关
60 | --包Microsoft.AspNet.WebApi.Owin
61 | 含:System.Web.Http.Owin.dll
62 | --包Microsoft.AspNet.WebApi.OwinSelfHost
63 | 无dll文件,依赖Microsoft.AspNet.WebApi.Owin ,Microsoft.Owin.Host.HttpListener,Microsoft.Owin.Hosting
--------------------------------------------------------------------------------
/webapi/webapi.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Debug
7 | AnyCPU
8 |
9 |
10 | 2.0
11 | {7A63DCA6-5F3F-440A-934E-A92C162C0E82}
12 | {349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}
13 | Library
14 | Properties
15 | webapi
16 | webapi
17 | v4.6.1
18 | true
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | 12.0
28 |
29 |
30 | true
31 | full
32 | false
33 | bin\
34 | DEBUG;TRACE
35 | prompt
36 | 4
37 |
38 |
39 | true
40 | pdbonly
41 | true
42 | bin\
43 | TRACE
44 | prompt
45 | 4
46 |
47 |
48 |
49 | ..\packages\Autofac.3.5.0\lib\net40\Autofac.dll
50 |
51 |
52 | ..\packages\Autofac.Owin.4.1.0\lib\net45\Autofac.Integration.Owin.dll
53 |
54 |
55 | ..\packages\Autofac.WebApi2.4.1.0\lib\net45\Autofac.Integration.WebApi.dll
56 |
57 |
58 | ..\packages\Autofac.WebApi2.Owin.4.0.0\lib\net45\Autofac.Integration.WebApi.Owin.dll
59 |
60 |
61 | ..\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.dll
62 |
63 |
64 | ..\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.SqlServer.dll
65 |
66 |
67 | ..\packages\JWT.3.1.1\lib\net46\JWT.dll
68 |
69 |
70 | ..\packages\log4net.2.0.8\lib\net45-full\log4net.dll
71 |
72 |
73 | ..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.1.0.7\lib\net45\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.dll
74 |
75 |
76 |
77 | ..\packages\Microsoft.Owin.3.1.0\lib\net45\Microsoft.Owin.dll
78 |
79 |
80 | ..\packages\Microsoft.Owin.Diagnostics.3.1.0\lib\net45\Microsoft.Owin.Diagnostics.dll
81 |
82 |
83 | ..\packages\Microsoft.Owin.Host.HttpListener.3.1.0\lib\net45\Microsoft.Owin.Host.HttpListener.dll
84 |
85 |
86 | ..\packages\Microsoft.Owin.Host.SystemWeb.3.1.0\lib\net45\Microsoft.Owin.Host.SystemWeb.dll
87 |
88 |
89 | ..\packages\Microsoft.Owin.Hosting.3.1.0\lib\net45\Microsoft.Owin.Hosting.dll
90 |
91 |
92 | ..\packages\MySql.Data.6.9.10\lib\net45\MySql.Data.dll
93 |
94 |
95 | ..\packages\MySql.Data.Entity.6.9.10\lib\net45\MySql.Data.Entity.EF6.dll
96 |
97 |
98 | ..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll
99 |
100 |
101 | ..\packages\Owin.1.0\lib\net40\Owin.dll
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | ..\packages\Microsoft.AspNet.WebApi.Client.5.2.3\lib\net45\System.Net.Http.Formatting.dll
110 |
111 |
112 |
113 |
114 |
115 | ..\packages\System.ValueTuple.4.4.0\lib\net461\System.ValueTuple.dll
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 | ..\packages\Microsoft.AspNet.WebApi.Core.5.2.3\lib\net45\System.Web.Http.dll
128 |
129 |
130 | ..\packages\Microsoft.AspNet.WebApi.Owin.5.2.3\lib\net45\System.Web.Http.Owin.dll
131 |
132 |
133 | ..\packages\Microsoft.AspNet.WebApi.WebHost.5.2.3\lib\net45\System.Web.Http.WebHost.dll
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 | Designer
146 |
147 |
148 | Designer
149 |
150 |
151 |
152 | Web.config
153 |
154 |
155 | Web.config
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 | Global.asax
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 | 10.0
207 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 | False
217 | True
218 | 10879
219 | /
220 | http://localhost:10879/
221 | False
222 | True
223 |
224 |
225 | False
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 | 这台计算机上缺少此项目引用的 NuGet 程序包。使用“NuGet 程序包还原”可下载这些程序包。有关更多信息,请参见 http://go.microsoft.com/fwlink/?LinkID=322105。缺少的文件是 {0}。
236 |
237 |
238 |
239 |
240 |
247 |
--------------------------------------------------------------------------------
/webapi/webapi.csproj.user:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | true
5 |
6 |
7 |
8 |
9 |
10 |
11 | ApiControllerEmptyScaffolder
12 | root/Controller
13 | 600
14 | True
15 | False
16 | True
17 |
18 | False
19 | ShowAllFiles
20 | <_SelectedScaffolderID>ApiControllerEmptyScaffolder
21 | <_SelectedScaffolderCategoryPath>root/Common
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 | NoStartPage
30 | True
31 | False
32 | False
33 | False
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 | True
43 | True
44 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------