從零開始一起學Blazor WebAssembly 開發(5_1)權限控制初識


上篇講了Blazor WebAssembly 實現登錄以及獲取Token,本篇講一下如何實現在前端這塊的權限控制。

Blazor 實現權限控制主要實現以下兩個:

1、實現判斷是否有權限

2、打開沒有權限頁面跳轉到登錄頁面

3、沒有權限的菜單不顯示

記住一點,客戶端實現的權限控制不是真的控制不能使用某個功能,因為客戶端是可以被破解的。所以想控制某個功能不被使用還是要依靠服務后端來控制。

現在依次說下以上三點如何實現,第一點我利用了asp.net core 和 blazor提供的基於策略的權限框架。做了一些改造,主要是因為完全使用他們那種與我做的Token機制不能有效結合,還好比較容易擴展。改造的地方也不多。主要是做了一個自定義的CustomAuthStateProvider

public class CustomAuthStateProvider : AuthenticationStateProvider
    {
        private readonly TokenUtil _tokenUtil;
        public CustomAuthStateProvider(TokenUtil tokenUtil)
        {
            _tokenUtil = tokenUtil;
        }

        public override async Task<AuthenticationState> GetAuthenticationStateAsync()
        {
            try
            {
                string tokenJson = await _tokenUtil.GetAccessToken();
                if (!String.IsNullOrEmpty(tokenJson))
                {
                    TokenInfo token = JsonConvert.DeserializeObject<TokenInfo>(tokenJson);

                    if (token != null && token.TokenExpire>DateTime.Now)
                    {
                        var authenticatedUser = new ClaimsPrincipal(new ClaimsIdentity(ParseClaimsFromJwt(token.TokenValue), "jwt"));
                        return new AuthenticationState(authenticatedUser);
                    }
                    else
                    {
                        return new AuthenticationState(new ClaimsPrincipal());
                    }
                }
                else
                {
                    return new AuthenticationState(new ClaimsPrincipal());
                }
            }
            catch (Exception ex)
            {
                return new AuthenticationState(new ClaimsPrincipal());
            }
        }
        public void NotifyAuthenticationState()
        {
            NotifyAuthenticationStateChanged(GetAuthenticationStateAsync());
        }
        private IEnumerable<Claim> ParseClaimsFromJwt(string jwt)
        {
            var claims = new List<Claim>();
            var payload = jwt.Split('.')[1];
            var jsonBytes = System.Text.Encoding.Default.GetString(ParseBase64WithoutPadding(payload));
            var keyValuePairs = JsonConvert.DeserializeObject<Dictionary<string, object>>(jsonBytes);
            keyValuePairs.TryGetValue(JwtClaimTypes.Name, out object roles);
            if (roles != null)
            {
                if (roles.ToString().Trim().StartsWith("["))
                {
                    var parsedRoles = JsonConvert.DeserializeObject<string[]>(roles.ToString());
                    foreach (var parsedRole in parsedRoles)
                    {
                        claims.Add(new Claim(JwtClaimTypes.Name, parsedRole));
                    }
                }
                else
                {
                    claims.Add(new Claim(JwtClaimTypes.Name, roles.ToString()));
                }
                keyValuePairs.Remove(JwtClaimTypes.Name);
            }
            claims.AddRange(keyValuePairs.Select(kvp => new Claim(kvp.Key, kvp.Value.ToString())));
            return claims;
        }
        private byte[] ParseBase64WithoutPadding(string base64)
        {
            switch (base64.Length % 4)
            {
                case 2: base64 += "=="; break;
                case 3: base64 += "="; break;
            }
            return Convert.FromBase64String(base64);
        }
    }

 

這里邊主要是通過 重寫了
GetAuthenticationStateAsync方法來返回Token所具有的權限,代碼僅供參考。因為ParseClaimsFromJwt 這步解析的我僅僅把JwtClaimTypes.Name的解析出來了。並沒有完全解析出來。使用的時候根據自己個人情況來寫權限策略。

寫好這個后在program里寫一下注入相關的代碼

 public static async Task Main(string[] args)
        {
            var builder = WebAssemblyHostBuilder.CreateDefault(args);
            builder.RootComponents.Add<App>("app");

            //builder.Services.AddTransient<CryptoKeyConfig>();
            builder.Services.AddTransient(sp => new HttpClient { BaseAddress = new Uri("https://localhost:44319") });
            builder.Services.AddTransient<TokenUtil>();
            builder.Services.AddTransient<TokenHttpClient>();
            builder.Services.AddScoped<CustomAuthStateProvider>();
            builder.Services.AddScoped<AuthenticationStateProvider>(s => s.GetRequiredService<CustomAuthStateProvider>());
            //在wasm中沒有默認配置,所以需要設置一下
            builder.Services.AddOptions();
            builder.Services.AddAuthorizationCore();
            builder.Services.AddAntDesign();
            builder.Services.AddOidcAuthentication(options =>
            {
                // Configure your authentication provider options here.
                // For more information, see https://aka.ms/blazor-standalone-auth
                builder.Configuration.Bind("Local", options.ProviderOptions);
            });
            await builder.Build().RunAsync();
        }
share 文件夾下加一下無權限時跳轉到登錄頁面的一個layout 名字叫 RedirectToLogin.razor

@inject NavigationManager Navigation
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication
@code {
    protected override void OnInitialized()
    {
        Navigation.NavigateTo($"/login?returnUrl={Uri.EscapeDataString(Navigation.Uri)}");
    }
}

 

App.razor 改成如下

注意,這里實現了沒有權限時跳轉到登錄窗口的代碼

<Router AppAssembly="@typeof(Program).Assembly">
    <Found Context="routeData">
        <AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)">
            <NotAuthorized>
                @if (!context.User.Identity.IsAuthenticated)
                {
                    <RedirectToLogin />
                }
                else
                {
                    <p>You are not authorized to access this resource.</p>
                }
            </NotAuthorized>
        </AuthorizeRouteView>
    </Found>
    <NotFound>
        <LayoutView Layout="@typeof(MainLayout)">
            <p>Sorry, there's nothing at this address.</p>
        </LayoutView>
    </NotFound>
</Router>
<AntContainer /> 

這樣基本的權限控制就實現了。下篇講一下如何用AuthorizeView 組件 和[Authorize] 屬性 實現頁面內組件和頁面本身的權限控制。也就是開頭說的2 和 3如何實現。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM