一个简单的博客

0%

Deploy in Ubuntu –dev

System Init

阿里云上的ECS

数据盘挂载【按需操作】

1
2
3
4
5
6
$ sudo apt update
$ sudo apt upgrade -y
$ sudo apt autoremove
$ sudo fdisk -l
# 执行命令后,如果不存在/dev/vdb,表示您的实例没有数据盘。确认数据盘是否已挂载。
$ sudo fdisk -u /dev/vdb # 执行后输入n开始新分区,后续均可回车使用默认值,最后输入 w 保存

运行fdisk -lu /dev/vdb命令查看新分区。
如果出现/dev/vdb1的相关信息,表示新分区已创建完成。
sudo mkfs -t ext4 /dev/vdb1 创建文件系统
sudo cp /etc/fstab /etc/fstab.bak 备份etc/fstab
把分区写入启动/etc/fstab,启动开机自动挂载分区

1
2

sudo echo `blkid /dev/vdb1 | awk '{print $2}' | sed 's/\"//g'` /mnt ext4 defaults 0 0 >> /etc/fstab

运行sudo mount /dev/vdb1 /mnt命令挂载文件系统。
如果运行df -h命令后出现新建文件系统的信息,表示文件系统挂载成功。

Install docker & 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

#Install
$ sudo apt-get update

$ sudo apt-get install \
apt-transport-https \
ca-certificates \
curl \
gnupg-agent \
software-properties-common

$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -

$ sudo add-apt-repository \
"deb [arch=amd64] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) \
stable"

$ sudo apt-get update
$ sudo apt-get install docker-ce docker-ce-cli containerd.io

#安装docker-compose
#请前往 https://github.com/docker/compose/releases 寻找最新版本的版本号进行替换,底下版本号为:1.27.2]
$ sudo curl -L "https://github.com/docker/compose/releases/download/1.27.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
$ sudo chmod +x /usr/local/bin/docker-compose
$ sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose
$ docker-compose --version

#修改存储路径
$ sudo service docker stop
$ sudo mv /var/lib/docker /mnt/docker
$ sudo ln -s /mnt/docker /var/lib/
$ sudo service docker start

#阿里云镜像地址登录阿里云后从https://cr.console.aliyun.com/cn-shenzhen/instances/mirrors获取
$ sudo mkdir -p /etc/docker
$ sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["https://u9bgtozi.mirror.aliyuncs.com"]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker

安装nginx以及配置

nginx安装

sudo apt install nginx

证书申请以及安装

1
2
3
4
5
sudo add-apt-repository universe  
sudo apt-get update
sudo apt-get install certbot python3-certbot-nginx
#开始申请证书
certbot --nginx --domains sudomain.***.com

更新nginx配置

覆盖上传nginx文件夹的文件到/etc/nginx

1
2
3
4
5
sudo rm /etc/nginx/sites-available/default
sudo rm /etc/nginx/sites-enabled/default
#域名
sudo ln -s /etc/nginx/sites-available/sudomain.***.com.conf /etc/nginx/sites-enabled/sudomain.***.com.conf
sudo service nginx restart

abp vnext 学习第一弹

dotnet tool install -g Volo.Abp.Cli

abpcli

创建没有手机端的项目

abp new Acme.BookStore -t app -d ef -u mvc -m none

等同命令

abp new Acme.BookStore -template app --database-provider ef --ui mvc --mobile none

修改对应项目连接字符串

修改Acme.BookStore.DbMigrator和Web项目的appsetting的连接字符串

数据迁移

执行Acme.BookStore.DbMigrator项目

或者,您可以在Visual Studio的程序包管理器控制台中运行Update-Database命令以应用迁移。

执行web项目

通用枚举遍历

如果你确认你的枚举类型是 int32,可以使用以下方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

/// <summary>
/// 将一个指定的枚举定义(Int)转换成集合对象.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static void ToList<T>() where T:Enum
{
Array datas = Enum.GetValues(typeof(T));

for (var i = 0; i < datas.Length; i++)
{
object value = datas.GetValue(i);
Console.WriteLine("{0,-9} {1}", value + ":",
(int)value);
//需要注意的是这里的强转(int)value ,所以这里需要你事先确认枚举基础类型,如果是byte,此处会抛出异常
}
}

有些时候你不能确认枚举的类型是int还是byte

demo示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

enum BEnum : byte
{
A ,
B ,
C
}

var type = typeof(BEnum);
FieldInfo[] fields = type.GetFields();
foreach (var field in fields)
{
if (field.FieldType!= type)
{
continue;
}
Console.WriteLine("{0,-9} {1}", field.Name + ":",
field.GetRawConstantValue());
//field.GetRawConstantValue() 获取数值
//field.Name 获取枚举值名称

}

输出结果

1
2
3
4

A: 0
B: 1
C: 2

C#控制台启动程序或者执行命令

C#启动程序

调用示例

1
StartAConsoleProcess(ygcApiExePath);
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
private static void StartAConsoleProcess(string exePath, params string[] cmdText)
{
Process p = new Process();
p.StartInfo = new ProcessStartInfo(exePath);
p.StartInfo.UseShellExecute = false; //是否使用操作系统shell启动

if (exePath.Contains(Path.DirectorySeparatorChar))
{
var file = new FileInfo(exePath);
p.StartInfo.WorkingDirectory = file.Directory.FullName;
}

p.StartInfo.RedirectStandardInput = true;//接受来自调用程序的输入信息
p.StartInfo.RedirectStandardOutput = true;//由调用程序获取输出信息
p.StartInfo.RedirectStandardError = true;//重定向标准错误输出
p.StartInfo.CreateNoWindow = true;//不显示程序窗口
p.OutputDataReceived += YgcApi_OutputDataReceived;
p.ErrorDataReceived += YgcApi_ErrorDataReceived;
p.Start();
p.BeginOutputReadLine();
p.BeginErrorReadLine();
if (cmdText.Length > 0)
{
foreach (var item in cmdText)
{
p.StandardInput.WriteLine(item);
//向cmd窗口发送输入信息
p.StandardInput.AutoFlush = true;
}
}
}

调用cmd执行命令

调用示例:

1
RunCommand("cd D:\\viewerSevice", "d:", "dotnet Ygc.Service.3DViewerService.dll");
1
2
3
4
private static void RunCommand(params string[] cmdText)
{
StartAConsoleProcess(exePath: "cmd.exe", cmdText);
}

windows删除无法访问的文件

现象

通过文件/文件夹的属性的安全选项卡,无法修改所有者和权限

界面上提示:win10 你没有权限查看或编辑这个对象

解决办法

按下 (Windows 徽标键 +X键),点击“命令提示符(管理员)”,复制以下命令并在命令提示符中点击鼠标右键,点击“粘贴”,随后按回车键(Enter)执行:

del /Q '待删除文件路径'

命令完成后重新启动计算机,查看截图3.png是否依然存在,如果存在,再次打开命令提示符(管理员)复制以下命令并粘贴执行:

1
ATTRIB 待删除文件路径>%temp%\pngA.txt & FSUTIL usn readdata 待删除文件路径>>%temp%\pngA.txt & icacls 待删除文件路径>>%temp%\pngA.txt & %temp%\pngA.txt

我试过执行完第一个命令,然后重启电脑就正常了。

参考文档

当前环境

  1. Net Core 2.2+ //建议使用Net Core 3.0

  2. Asp.Net Framework 4.6.2+

  3. Visual Studio 2019//如果使用Net Core 3.0,你可能需要预览版

新增空的解决方案

  1. 打开命令行。执行dotnet new sln -n SsoTest 建立空白解决方案。

新增Identity Server4 服务端【本文不讨论服务端配置问题】

新增项目并添加到解决方案里

  1. 打开命令行或者powershell

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    # 新增IdentityServer4模板
    dotnet new -i IdentityServer4.Templates
    # 新建项目
    dotnet new is4empty -n IdentityServer
    # 添加到解决方案
    dotnet sln add .\IdentityServer\IdentityServer.csproj
    # 进入项目目录
    cd IdentityServer
    # 添加UI
    dotnet new is4ui
  2. 修改Config.cs文件

    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
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    using IdentityServer4;
    using IdentityServer4.Models;
    using IdentityServer4.Test;
    using System.Collections.Generic;
    using System.Security.Claims;

    namespace IdentityServer
    {
    public static class Config
    {
    public static IEnumerable<IdentityResource> GetIdentityResources()
    {
    return new IdentityResource[]
    {
    new IdentityResources.OpenId(),
    new IdentityResources.Profile(),
    new IdentityResources.Email(),
    new IdentityResources.Phone(),
    };
    }
    public static List<TestUser> GetUsers()
    {
    return new List<TestUser> {
    new TestUser {
    SubjectId = "1",
    Username = "alice",
    Password = "password",
    Claims = new []
    {
    new Claim("name", "Alice"),
    new Claim("website", "https://alice.com")
    }
    },
    new TestUser {
    SubjectId = "2",
    Username = "bob",
    Password = "password",
    Claims = new []
    {
    new Claim("name", "Bob"),
    new Claim("website", "https://bob.com")
    }
    }
    };
    }
    public static IEnumerable<ApiResource> GetApis()
    {
    return new ApiResource[] {
    new ApiResource ("api1", "My API")
    };
    }

    public static IEnumerable<Client> GetClients()
    {
    var secret = "49C1A7E1-0C79-4A89-A3D6-A37998FB86B0".Sha256();
    return new Client[] {
    new Client {
    ClientId = "mvc",
    ClientName = "MVC Client",
    ClientSecrets = {
    new Secret (secret)
    },
    AllowedGrantTypes = GrantTypes.HybridAndClientCredentials,
    // where to redirect to after login
    RedirectUris = { "http://localhost:5001/signin-oidc" },
    // where to redirect to after logout
    PostLogoutRedirectUris = { "http://localhost:5001/signout-callback-oidc" },
    AllowedScopes = new List<string> {
    IdentityServerConstants.StandardScopes.OpenId,
    IdentityServerConstants.StandardScopes.Email,
    IdentityServerConstants.StandardScopes.Phone,
    IdentityServerConstants.StandardScopes.Profile,
    "api1"
    }
    },,
    new Client
    {
    ClientName = "vuejs",
    ClientId = "vuejsclient",
    AllowedGrantTypes=GrantTypes.Implicit,
    AllowAccessTokensViaBrowser=true,
    AccessTokenType = AccessTokenType.Reference,
    UpdateAccessTokenClaimsOnRefresh = true,
    AllowOfflineAccess = true,
    RequireConsent = false,
    //AccessTokenLifetime = 50,
    RedirectUris = new List<string>()
    {
    "http://localhost:5002/static/callback.html",
    "http://localhost:5002/static/silent-renew.html"
    },
    PostLogoutRedirectUris = {
    "http://localhost:5002/index.html"
    },
    AllowedCorsOrigins = {
    "http://localhost:5002"
    },
    AllowedScopes =
    {
    IdentityServerConstants.StandardScopes.OpenId,
    IdentityServerConstants.StandardScopes.Profile,
    IdentityServerConstants.StandardScopes.Address,
    "roles",
    "identityserver4api",
    "country",
    "subscriptionlevel"
    },
    ClientSecrets =
    {
    new Secret("secret".Sha256())
    }
    }
    };
    }
    }
    }
  3. 修改Startup.cs,取消注释

    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
    public IHostingEnvironment Environment { get; }

    public Startup(IHostingEnvironment environment)
    {
    Environment = environment;
    }

    public void ConfigureServices(IServiceCollection services)
    {
    // uncomment, if you want to add an MVC-based UI
    services.AddMvc().SetCompatibilityVersion(Microsoft.AspNetCore.Mvc.CompatibilityVersion.Version_2_1);

    var builder = services.AddIdentityServer()
    .AddInMemoryIdentityResources(Config.GetIdentityResources())
    .AddInMemoryApiResources(Config.GetApis())
    .AddInMemoryClients(Config.GetClients())
    .AddTestUsers(Config.GetUsers());

    if (Environment.IsDevelopment())
    {
    builder.AddDeveloperSigningCredential();
    }
    else
    {
    throw new Exception("need to configure key material");
    }
    }

    public void Configure(IApplicationBuilder app)
    {
    if (Environment.IsDevelopment())
    {
    app.UseDeveloperExceptionPage();
    }

    // uncomment if you want to support static files
    app.UseStaticFiles();

    app.UseIdentityServer();
    // uncomment, if you want to add an MVC-based UI
    app.UseMvcWithDefaultRoute();
    }
  4. 新开一个命令行或者powershell窗口,运行服务端

    1
    2
    3
    4
    5
    6

    # 还原nuget,编译
    dotnet restore
    dotnet build
    # 运行
    dotnet run

新增一个空的WebApi项目

  1. 打开解决方案SsoTest.sln
  2. 在解决方案上右键->添加->新建项目,创建WebApi项目,名为SSOTest.WebApi



配置WebApi接入Identity Server

  1. 修改项目属性,指定web端口为5002

  2. 打开包控制台,安装nuget包

    1
    2
    3
    Install-Package IdentityModel -Version 3.10.10
    Install-Package IdentityServer3.Contrib.AccessTokenValidation
    Install-Package Microsoft.Owin.Host.SystemWeb
  3. 新增OWIN的Startup.cs文件

  4. 修改为Startup.cs文件.

    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
    using System;
    using System.Net.Http;
    using System.Security.Claims;
    using System.Web.Helpers;
    using IdentityModel;
    using IdentityModel.Client;
    using Microsoft.Owin;
    using Microsoft.Owin.Security;
    using Microsoft.Owin.Security.Cookies;
    using Microsoft.Owin.Security.OpenIdConnect;
    using Owin;

    [assembly: OwinStartup(typeof(SSOTest.Client.Startup))]

    namespace SSOTest.Client
    {
    public partial class Startup
    {
    public void Configuration(IAppBuilder app)
    {
    // 有关如何配置应用程序的详细信息,请访问 https://go.microsoft.com/fwlink/?LinkID=316888
    ConfigureAuth(app);
    }

    private void ConfigureAuth(IAppBuilder app)
    {
    var secret = "49C1A7E1-0C79-4A89-A3D6-A37998FB86B0".ToSha256();
    AntiForgeryConfig.UniqueClaimTypeIdentifier = "sub";
    app.UseIdentityServerBearerTokenAuthentication(new IdentityServerBearerTokenAuthenticationOptions
    {
    Authority = "http://localhost:5000",
    RequiredScopes = new string[] { "webapi" },
    ClientId = "Test",
    ClientSecret = secret,
    DelayLoadMetadata = true
    });
    }
    }
    }
  5. 打开Controllers/ValuesController.cs
    Get这个Action上加特性[Authorize]

  6. 运行Client项目,访问 http://localhost:5002/Api/Values

源码下载

on the github

新建空白分支

  1. 创建一个不跟踪历史分支

    1
    git checkout --orphan <new_branch>
  2. 清空文件

    1
    2
    git rm --cached -r .
    git clean -f -d
  3. 创建空的commit

    1
    git commit --allow-empty -m "[empty] initial commit"
  4. 推送新分支

    1
    git push origin <new_branch>

配置启用OssFs

  1. 源码编译安装

    1
    2
    3
    4
    5
    6
    7
    sudo apt install automake autotools-dev g++ git libcurl4-gnutls-dev libfuse-dev libssl-dev libxml2-dev make pkg-config
    git clone https://github.com/aliyun/ossfs.git
    cd ossfs
    ./autogen.sh
    ./configure
    make
    sudo make install
  2. 配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    #密码配置
    echo my-bucket:my-access-key-id:my-access-key-secret > /etc/passwd-ossfs
    #多个节点请直接编辑文件,一行一个记录
    chmod 640 /etc/passwd-ossfs
    #目录准备
    mkdir /mnt/ossfs
    sudo vi /etc/fstab
    ## 添加以下行到文件尾,保存后退出
    # ossfs#your_bucket_name your_mount_point fuse _netdev,url=your_url,allow_other 0 0
    ## your_url http://oss-cn-shenzhen-internal.aliyuncs.com 深圳节点内部访问【节约流量】
    sudo mount -a
    # df -h 可以查看挂载详情
  3. 添加cloud目录供NextCloud专门存储

    1
    2
    sudo chown -R www-data:www-data /mnt/ossfs/nextcloud
    sudo chmod -R 770 /mnt/ossfs/nextcloud/

安装NextCloud

  1. 安装环境

    1
    2
    3
    4
    5
    6
    7
    8
    9
    # nginx安装
    sudo apt install nginx
    # nginx -v 查看是否安装成功
    #php 安装
    sudo apt install php7.2-fpm
    # php -v 查看安装信息
    sudo apt-get install mariadb-server
    sudo apt-get install php7.2-gd php7.2-json php7.2-mysql php7.2-curl php7.2-mbstring
    sudo apt-get install php7.2-intl php-imagick php7.2-xml php7.2-zip php-redis php-apcu php7.2-bz2
  2. 环境配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    # mysql 配置
    sudo mysql_secure_installation
    #出现一堆配置 ,按需选择
    # nginx php 配置请自行寻找 或者直接用apache
    # mysql -u root -p
    # CREATE USER 'username'@'localhost' IDENTIFIED BY 'password';
    # CREATE DATABASE IF NOT EXISTS nextcloud;
    # GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTER, CREATE TEMPORARY TABLES ON nextcloud.* TO 'username'@'localhost' IDENTIFIED BY 'password';
    # FLUSH privileges;
  3. 下载Nextcloud安装

    1
    2
    3
    4
    5
    wget https://download.nextcloud.com/server/releases/nextcloud-16.0.5.tar.bz2
    tar -xjf nextcloud-x.y.z.tar.bz2
    cp -r nextcloud /var/www #cp -r nextcloud /path/to/webserver/document-root
    sudo chown -R www-data:www-data /var/www/nextcloud
    sudo chmod -R 770 /var/www/nextcloud/

    打开网站访问,按照要求输入信息

配置NextCloud

附件

nginx配置文件样本

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
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
upstream php-handler {
#server 127.0.0.1:9000;
server unix:/var/run/php/php7.2-fpm.sock;
}

server {
listen 80;
listen [::]:80;
server_name cloud.xyfy.pub;
# enforce https
return 301 https://$server_name:443$request_uri;
}

server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name cloud.xyfy.pub;

# Use Mozilla's guidelines for SSL/TLS settings
# https://mozilla.github.io/server-side-tls/ssl-config-generator/
# NOTE: some settings below might be redundant
ssl_certificate cert/2879191_cloud.xyfy.pub.pem;
ssl_certificate_key cert/2879191_cloud.xyfy.pub.key;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

# Add headers to serve security related headers
# Before enabling Strict-Transport-Security headers please read into this
# topic first.
#add_header Strict-Transport-Security "max-age=15768000; includeSubDomains; preload;";
#
# WARNING: Only add the preload option once you read about
# the consequences in https://hstspreload.org/. This option
# will add the domain to a hardcoded list that is shipped
# in all major browsers and getting removed from this list
# could take several months.
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
add_header X-Robots-Tag none;
add_header X-Download-Options noopen;
add_header X-Permitted-Cross-Domain-Policies none;
add_header Referrer-Policy no-referrer;

# Remove X-Powered-By, which is an information leak
fastcgi_hide_header X-Powered-By;

# Path to the root of your installation
root /var/www/nextcloud;

location = /robots.txt {
allow all;
log_not_found off;
access_log off;
}

# The following 2 rules are only needed for the user_webfinger app.
# Uncomment it if you're planning to use this app.
#rewrite ^/.well-known/host-meta /public.php?service=host-meta last;
#rewrite ^/.well-known/host-meta.json /public.php?service=host-meta-json last;

# The following rule is only needed for the Social app.
# Uncomment it if you're planning to use this app.
#rewrite ^/.well-known/webfinger /public.php?service=webfinger last;

location = /.well-known/carddav {
return 301 $scheme://$host:$server_port/remote.php/dav;
}
location = /.well-known/caldav {
return 301 $scheme://$host:$server_port/remote.php/dav;
}

# set max upload size
client_max_body_size 512M;
fastcgi_buffers 64 4K;

# Enable gzip but do not remove ETag headers
gzip on;
gzip_vary on;
gzip_comp_level 4;
gzip_min_length 256;
gzip_proxied expired no-cache no-store private no_last_modified no_etag auth;
gzip_types application/atom+xml application/javascript application/json application/ld+json application/manifest+json application/rss+xml application/vnd.geo+json application/vnd.ms-fontobject application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/bmp image/svg+xml image/x-icon text/cache-manifest text/css text/plain text/vcard text/vnd.rim.location.xloc text/vtt text/x-component text/x-cross-domain-policy;

# Uncomment if your server is build with the ngx_pagespeed module
# This module is currently not supported.
#pagespeed off;

location / {
rewrite ^ /index.php$request_uri;
}

location ~ ^\/(?:build|tests|config|lib|3rdparty|templates|data)\/ {
deny all;
}
location ~ ^\/(?:\.|autotest|occ|issue|indie|db_|console) {
deny all;
}

location ~ ^\/(?:index|remote|public|cron|core\/ajax\/update|status|ocs\/v[12]|updater\/.+|oc[ms]-provider\/.+)\.php(?:$|\/) {
fastcgi_split_path_info ^(.+?\.php)(\/.*|)$;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_param HTTPS on;
# Avoid sending the security headers twice
fastcgi_param modHeadersAvailable true;
# Enable pretty urls
fastcgi_param front_controller_active true;
fastcgi_pass php-handler;
fastcgi_intercept_errors on;
fastcgi_request_buffering off;
}

location ~ ^\/(?:updater|oc[ms]-provider)(?:$|\/) {
try_files $uri/ =404;
index index.php;
}

# Adding the cache control header for js, css and map files
# Make sure it is BELOW the PHP block
location ~ \.(?:css|js|woff2?|svg|gif|map)$ {
try_files $uri /index.php$request_uri;
add_header Cache-Control "public, max-age=15778463";
# Add headers to serve security related headers (It is intended to
# have those duplicated to the ones above)
# Before enabling Strict-Transport-Security headers please read into
# this topic first.
#add_header Strict-Transport-Security "max-age=15768000; includeSubDomains; preload;";
#
# WARNING: Only add the preload option once you read about
# the consequences in https://hstspreload.org/. This option
# will add the domain to a hardcoded list that is shipped
# in all major browsers and getting removed from this list
# could take several months.
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
add_header X-Robots-Tag none;
add_header X-Download-Options noopen;
add_header X-Permitted-Cross-Domain-Policies none;
add_header Referrer-Policy no-referrer;

# Optional: Don't log access to assets
access_log off;
}

location ~ \.(?:png|html|ttf|ico|jpg|jpeg|bcmap)$ {
try_files $uri /index.php$request_uri;
# Optional: Don't log access to other assets
access_log off;
}
}

当前环境

  1. Net Core 2.2+ //建议使用Net Core 3.0

  2. Asp.Net Framework 4.6.2+

  3. Visual Studio 2019//如果使用Net Core 3.0,你可能需要预览版

新增空的解决方案

  1. 打开命令行。执行dotnet new sln -n SsoTest 建立空白解决方案。

新增Identity Server4 服务端【本文不讨论服务端配置问题】

新增项目并添加到解决方案里

  1. 打开命令行或者powershell

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    # 新增IdentityServer4模板
    dotnet new -i IdentityServer4.Templates
    # 新建项目
    dotnet new is4empty -n IdentityServer
    # 添加到解决方案
    dotnet sln add .\IdentityServer\IdentityServer.csproj
    # 进入项目目录
    cd IdentityServer
    # 添加UI
    dotnet new is4ui
  2. 修改Config.cs文件

    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
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    using IdentityServer4;
    using IdentityServer4.Models;
    using IdentityServer4.Test;
    using System.Collections.Generic;
    using System.Security.Claims;

    namespace IdentityServer
    {
    public static class Config
    {
    public static IEnumerable<IdentityResource> GetIdentityResources()
    {
    return new IdentityResource[]
    {
    new IdentityResources.OpenId(),
    new IdentityResources.Profile(),
    new IdentityResources.Email(),
    new IdentityResources.Phone(),
    };
    }
    public static List<TestUser> GetUsers()
    {
    return new List<TestUser> {
    new TestUser {
    SubjectId = "1",
    Username = "alice",
    Password = "password",
    Claims = new []
    {
    new Claim("name", "Alice"),
    new Claim("website", "https://alice.com")
    }
    },
    new TestUser {
    SubjectId = "2",
    Username = "bob",
    Password = "password",
    Claims = new []
    {
    new Claim("name", "Bob"),
    new Claim("website", "https://bob.com")
    }
    }
    };
    }
    public static IEnumerable<ApiResource> GetApis()
    {
    return new ApiResource[] {
    new ApiResource ("api1", "My API")
    };
    }

    public static IEnumerable<Client> GetClients()
    {
    var secret = "49C1A7E1-0C79-4A89-A3D6-A37998FB86B0".Sha256();
    return new Client[] {
    new Client {
    ClientId = "mvc",
    ClientName = "MVC Client",
    ClientSecrets = {
    new Secret (secret)
    },
    AllowedGrantTypes = GrantTypes.HybridAndClientCredentials,
    // where to redirect to after login
    RedirectUris = { "http://localhost:5001/signin-oidc" },
    // where to redirect to after logout
    PostLogoutRedirectUris = { "http://localhost:5001/signout-callback-oidc" },
    AllowedScopes = new List<string> {
    IdentityServerConstants.StandardScopes.OpenId,
    IdentityServerConstants.StandardScopes.Email,
    IdentityServerConstants.StandardScopes.Phone,
    IdentityServerConstants.StandardScopes.Profile,
    "api1"
    }
    }};
    }
    }
    }
  3. 修改Startup.cs,取消注释

    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
    public IHostingEnvironment Environment { get; }

    public Startup(IHostingEnvironment environment)
    {
    Environment = environment;
    }

    public void ConfigureServices(IServiceCollection services)
    {
    // uncomment, if you want to add an MVC-based UI
    services.AddMvc().SetCompatibilityVersion(Microsoft.AspNetCore.Mvc.CompatibilityVersion.Version_2_1);

    var builder = services.AddIdentityServer()
    .AddInMemoryIdentityResources(Config.GetIdentityResources())
    .AddInMemoryApiResources(Config.GetApis())
    .AddInMemoryClients(Config.GetClients())
    .AddTestUsers(Config.GetUsers());

    if (Environment.IsDevelopment())
    {
    builder.AddDeveloperSigningCredential();
    }
    else
    {
    throw new Exception("need to configure key material");
    }
    }

    public void Configure(IApplicationBuilder app)
    {
    if (Environment.IsDevelopment())
    {
    app.UseDeveloperExceptionPage();
    }

    // uncomment if you want to support static files
    app.UseStaticFiles();

    app.UseIdentityServer();
    // uncomment, if you want to add an MVC-based UI
    app.UseMvcWithDefaultRoute();
    }
  4. 新开一个命令行或者powershell窗口,运行服务端

    1
    2
    3
    4
    5
    6

    # 还原nuget,编译
    dotnet restore
    dotnet build
    # 运行
    dotnet run

新增一个空的MVC5项目

  1. 打开解决方案SsoTest.sln
  2. 在解决方案上右键->添加->新建项目,创建MVC5项目,名为SSOTest.Client



配置MVC5接入Identity Server

  1. 修改Client项目属性,指定web端口为5001

  2. 打开包控制台,安装nuget包

    1
    2
    3
    4
    Install-Package IdentityModel -Version 3.10.10
    Install-Package Microsoft.Owin.Security.Cookies
    Install-Package Microsoft.Owin.Security.OpenIdConnect
    Install-Package Microsoft.Owin.Host.SystemWeb
  3. 新增OWIN的Startup.cs文件

  4. 修改为Startup.cs文件.

    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
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    using System;
    using System.Net.Http;
    using System.Security.Claims;
    using System.Web.Helpers;
    using IdentityModel;
    using IdentityModel.Client;
    using Microsoft.Owin;
    using Microsoft.Owin.Security;
    using Microsoft.Owin.Security.Cookies;
    using Microsoft.Owin.Security.OpenIdConnect;
    using Owin;

    [assembly: OwinStartup(typeof(SSOTest.Client.Startup))]

    namespace SSOTest.Client
    {
    public partial class Startup
    {
    public void Configuration(IAppBuilder app)
    {
    ConfigureAuth(app);
    }

    public void ConfigureAuth(IAppBuilder app)
    {
    AntiForgeryConfig.UniqueClaimTypeIdentifier = "sub";

    app.UseCookieAuthentication(new CookieAuthenticationOptions
    {
    AuthenticationType = CookieAuthenticationDefaults.AuthenticationType,
    CookieHttpOnly = true
    });
    var secret = "49C1A7E1-0C79-4A89-A3D6-A37998FB86B0".ToSha256();
    app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
    {
    Authority = "http://localhost:5000",
    ClientId = "mvc",
    ClientSecret = secret,
    RedirectUri = "http://localhost:5001/signin-oidc",
    PostLogoutRedirectUri = "http://localhost:5001/signout-oidc",
    ResponseType = "code id_token",
    Scope = "openid profile",
    RequireHttpsMetadata = false,
    SignInAsAuthenticationType = CookieAuthenticationDefaults.AuthenticationType,
    Notifications = new OpenIdConnectAuthenticationNotifications
    {
    AuthorizationCodeReceived = async n =>
    {
    var client = new HttpClient();
    var disco = await client.GetDiscoveryDocumentAsync(new DiscoveryDocumentRequest()
    {
    Address = "http://localhost:5000",
    Policy ={
    RequireHttps = false
    }
    });
    if (disco.IsError) throw new Exception(disco.Error);
    // use the code to get the access and refresh token
    var tokenResponse = await client.RequestAuthorizationCodeTokenAsync(new AuthorizationCodeTokenRequest()
    {
    Address = disco.TokenEndpoint,
    ClientId = "mvc",
    ClientSecret = "49C1A7E1-0C79-4A89-A3D6-A37998FB86B0",
    Code = n.Code,
    RedirectUri = n.RedirectUri
    });

    if (tokenResponse.IsError)
    {
    throw new Exception(tokenResponse.Error);
    }
    // use the access token to retrieve claims from userinfo
    var userInfoResponse = await client.GetUserInfoAsync(new UserInfoRequest()
    {
    Address = disco.UserInfoEndpoint,
    Token = tokenResponse.AccessToken
    });

    // create new identity
    var id = new ClaimsIdentity(n.AuthenticationTicket.Identity.AuthenticationType);
    id.AddClaims(userInfoResponse.Claims);
    id.AddClaim(new Claim("access_token", tokenResponse.AccessToken));
    id.AddClaim(new Claim("expires_at", DateTime.UtcNow.AddSeconds(tokenResponse.ExpiresIn).ToString()));
    //id.AddClaim(new Claim("refresh_token", tokenResponse.RefreshToken));
    id.AddClaim(new Claim("id_token", n.ProtocolMessage.IdToken));
    id.AddClaim(new Claim("sid", n.AuthenticationTicket.Identity.FindFirst("sid").Value));

    n.AuthenticationTicket = new AuthenticationTicket(
    new ClaimsIdentity(id.Claims, n.AuthenticationTicket.Identity.AuthenticationType, "name", "role"),
    n.AuthenticationTicket.Properties);
    //TODO 本地USER同步
    foreach (var item in userInfoResponse.Claims)
    {
    if (item.Type == "sub")
    {
    }
    }
    }
    }
    });
    }
    }
    }
  5. 打开Controllers/HomeController.cs
    About这个Action上加特性[Authorize]

  6. 运行Client项目,访问 http://localhost:5001/Home/About

源码下载

on the github