frp 是一个专注于内网穿透的高性能的反向代理应用,支持 TCP、UDP、HTTP、HTTPS 等多种协议。可以将内网服务以安全、便捷的方式通过具有公网 IP 节点的中转暴露到公网
github源码:https://github.com/fatedier/frp/
官方文档:概览 | frp
frp一般是使用token认证,但是token过于简单,因此,决定使用oidc进行认证
OIDC 是 OpenID Connect 的简称,验证流程参考 Client Credentials Grant。
本次是基于Django进行搭建
依赖文件requirement.txt如下
- Django==4.1
- django-cors-headers==3.13.0
- django-oauth-toolkit==2.1.0
- djangorestframework==3.13.1
- PyJWT==2.4.0
主要使用的是 django-oauth-toolkit 模块,该模块官方文档 Welcome to Django OAuth Toolkit Documentation — Django OAuth Toolkit 2.1.0 documentation
然后根据文档进行操作
1.启动一个新的 Django 项目并"rest_framework"和"oauth2_provider"添加到您的INSTALLED_APPS设置中
- INSTALLED_APPS = (
- 'django.contrib.admin',
- ...
- 'oauth2_provider',
- 'rest_framework',
- )
2.现在我们需要告诉 Django REST Framework 使用新的身份验证后端。请在settings.py模块的末尾添加以下行
- REST_FRAMEWORK = {
- 'DEFAULT_AUTHENTICATION_CLASSES': (
- 'oauth2_provider.contrib.rest_framework.OAuth2Authentication',
- ),
- 'DEFAULT_PERMISSION_CLASSES': (
- 'rest_framework.permissions.IsAuthenticated',
- )
- }
3.创建一个简单的 API 来访问用户和组
- #!/usr/bin/env python
- # -*- coding:utf-8 -*-
- # project : xfrp
- # filename : oauth.py
- # author : ly_13
- # date : 2022/9/2
-
- from django.contrib.auth.models import User, Group
- from rest_framework import generics, permissions, serializers
-
- from oauth2_provider.contrib.rest_framework import TokenHasReadWriteScope, TokenHasScope
-
-
- # first we define the serializers
- class UserSerializer(serializers.ModelSerializer):
- class Meta:
- model = User
- fields = ('username', 'email', "first_name", "last_name")
-
-
- class GroupSerializer(serializers.ModelSerializer):
- class Meta:
- model = Group
- fields = ("name",)
-
-
- # Create the API views
- class UserList(generics.ListCreateAPIView):
- permission_classes = [permissions.IsAuthenticated, TokenHasReadWriteScope]
- queryset = User.objects.all()
- serializer_class = UserSerializer
-
-
- class UserDetails(generics.RetrieveAPIView):
- permission_classes = [permissions.IsAuthenticated, TokenHasReadWriteScope]
- queryset = User.objects.all()
- serializer_class = UserSerializer
-
-
- class GroupList(generics.ListAPIView):
- permission_classes = [permissions.IsAuthenticated, TokenHasScope]
- required_scopes = ['groups']
- queryset = Group.objects.all()
- serializer_class = GroupSerializer
- from django.contrib import admin
- from django.urls import path, include
-
- from api.views.oauth import UserList, UserDetails, GroupList
- admin.autodiscover()
- urlpatterns = [
- path("admin/", admin.site.urls),
- path('o/', include('oauth2_provider.urls', namespace='oauth2_provider')),
- path('users/', UserList.as_view()),
- path('users/
/' , UserDetails.as_view()), - path('groups/', GroupList.as_view()),
- ]
4,添加oauth配置信息 到settings.py模块中
OIDC还需要jwt令牌签名算法,使用的是rs256 非对称加密,因此还需要一个rsa私钥,可以通过openssl命令生成
openssl genrsa -out oidc.key 4096
注意:此密钥的内容必须保密。不要将它放在您的设置中并将其提交给版本控制!
- from oauthlib.oauth2.rfc6749.tokens import signed_token_generator
-
- OAUTH2_PROVIDER = {
- "OIDC_ENABLED": True,
- "OIDC_RSA_PRIVATE_KEY": open(os.path.join(BASE_DIR, 'oidc.key'), 'r').read(),
- "SCOPES": {
- "openid": "OpenID Connect scope",
- "read": 'Read scope',
- 'write': 'Write scope',
- 'groups': 'Access to your groups'
- },
- "EXTRA_SERVER_KWARGS": {
- "token_generator": signed_token_generator(open(os.path.join(BASE_DIR, 'oidc.key'), 'r').read(),
- **{'iss': 'http://10.66.8.100:8000/o', 'aud': 'openid'})
- },
- }
5,迁移数据,并创建管理员用户
- python manage.py migrate
- python manage.py createsuperuser
- python manage.py runserver
6,访问admin,然后注册新的应用程序
http://localhost:8000/admin
然后再访问
http://localhost:8000/o/applications/

- [common]
- bind_port = 8888
-
-
- authentication_method = oidc
- oidc_issuer = http://10.66.8.100:8000/o
- oidc_audience = openid
- [common]
- server_addr = 10.66.8.100
- server_port = 8888
-
- authentication_method = oidc
- oidc_client_id = Aybonk9RoG1I6YzTLKUDnPXU647rtJymig0I2PPj
- oidc_client_secret = fNPvjFbZv6Q0IUwE4o2QAHAWjtdlQbgO75RjU3IbKEdythlKmUu3ZShlO8juOhsxCbE7U9gAgXQTbUMsoXt5YrshAt2CwARSz63T5OfnbhwPNl8HtsdqfG2HMFRjMaCK
- oidc_audience = openid
- oidc_token_endpoint_url = http://10.66.8.100:8000/o/token/
-
-
-
- [nginx]
- type = tcp
- local_ip = 127.0.0.1
- local_port = 80
- remote_port = 29999

会发现django服务有请求如下

客户端也正常启动,没有报错

会发现django服务有请求如下
说明请求成功,客户端的端口也已经映射到服务器上面了
1.token格式不对,compact JWS format must have three parts
- 2022/09/05 08:28:55 [E] [service.go:340] invalid OIDC token in login: oidc: malformed jwt: square/go-jose: compact JWS format must have three parts
- 2022/09/05 08:28:55 [W] [service.go:128] login to server failed: invalid OIDC token in login: oidc: malformed jwt: square/go-jose: compact JWS format must have three parts
- invalid OIDC token in login: oidc: malformed jwt: square/go-jose: compact JWS format must have three parts
这个需要oidc返回jwt格式的数据,也就是 xxx.xxx.xxx 类似用电分割的加密数据
2.iss不一致,id token issued by a different provider
- 2022/09/05 08:30:50 [E] [service.go:340] invalid OIDC token in login: oidc: id token issued by a different provider, expected "http://127.0.0.1:8000/o" got ""
- 2022/09/05 08:30:50 [W] [service.go:128] login to server failed: invalid OIDC token in login: oidc: id token issued by a different provider, expected "http://127.0.0.1:8000/o" got ""
- invalid OIDC token in login: oidc: id token issued by a different provider, expected "http://127.0.0.1:8000/o" got ""
这个需要在生成jwt的时候,加上iss字段
3.audience不正确,oidc: expected audience "openid"
- 2022/09/05 08:32:17 [E] [service.go:340] invalid OIDC token in login: oidc: expected audience "openid" got []
- 2022/09/05 08:32:17 [W] [service.go:128] login to server failed: invalid OIDC token in login: oidc: expected audience "openid" got []
- invalid OIDC token in login: oidc: expected audience "openid" got []
这个需要在生成jwt的时候,加上openid字段,并且和配置文件保持一致