1. Authelia配置

  • ./data/authelia/config/configuration.yml

    ---
    ###############################################################
    #                   Authelia configuration                    #
    ###############################################################
    ​
    server:
      address: 'tcp://:9091'
    ​
    log:
      level: 'debug'
    ​
    # 基于时间的一次性密码配置
    totp:
      # 是否关闭
      disable: true
      # 身份验证器应用程序中显示的发行者名称
      issuer: 'authelia.com'
    ​
    # 访问控制(https://www.authelia.com/configuration/security/access-control/)
    # deny - 拒绝; bypass - 无需认证; one_factor - 用户名密码认证; two_factor - 二次认证
    access_control:
      # 默认策略
      default_policy: 'deny'
      # 规则
      rules:
        - domain: 'public.example.cn'
          policy: 'bypass'
        - domain: 'traefik.example.cn'
          policy: 'one_factor'
        - domain: 'secure.example.cn'
          policy: 'two_factor'
    ​
    # 身份验证方式配置
    identity_providers:
      # OIDC配置
      oidc:
        # 【需要修改】64位随机字符串,可以使用命令(tr -cd '[:alnum:]' < /dev/urandom | fold -w "64" | head -n 1 | tr -d '\n' ; echo)生成
        hmac_secret: 'nIrX4RIilsAIfxddUeamyizomGvRWL8rocVeJyCMD22jU4PTgdSFdMAnfqTC5lEL'
        jwks:
          - key_id: 'jwt_key'
            algorithm: 'RS256'
            use: 'sig'
            # 【需要修改】JWT加密key,可以使用命令(docker run --rm -u "$(id -u):$(id -g)" -v "$(pwd)":/keys authelia/authelia:4.39.3 authelia crypto pair rsa generate --directory /keys)生成到当前文件夹下,使用命令(cat ./private.pem)打印获取
            key: |
              -----BEGIN PRIVATE KEY-----
              MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDa7Bwk5EQJTkde
              9wrGGGFIgLM88Izc3wxAw6wstFlupeKBXifSuqSZm0y2TIdjf0578qyoTEU6bnCu
              GuwlEVTPovjgCLJ9jS1MynJ+vtb7cUe58PKyHygUF3M7WOMJKh36cGczMLdz/UE5
              rO0UcScomw0wsxri8+ggtJe4juzS75WdrKpvOM/6JAj0NZsCEdZLxM04JcrApxFo
              WmxyB+SldJzBkaK98xUKdphspptIvJJ/w3SEefTNagbVRzOHp8kCUkKMkUEFAVMH
              fZ68CJjC01rgIluyDmAMCDvwceHiRfDGlQYAwQtSz3YSexN22CJoCRJV5TdowFLL
              wYORnsGFAgMBAAECggEAIfcwBqlDxQ3YFOvPSBzQOyd8p5/KuxoAFKxHMkho97nV
              Aq1REGjU0OA/bqlA+DbwPlrQXuw2cXJhGSIkMTwXJH/sjRXMW55jwN5IFVIWmLxi
              nH69qNUcqsFYp4SK9QkLzadS26ZbUCuiywgBYIKNyMcFrOCc80lUYn6ia4gBRliM
              FEMwEXpknGJOEp8+AeAJUTnjGLmOCL6a7r6rcOCm+Odz7yqKyY9MnsAjh3vZ2th/
              QzwOdWjim9iNSMbEj/C5BqcQ1GmQW1hbdUPJBcvq8VwLsKiAErJYUIzf+lZjlLxh
              VJUXaemBCYXFOjqxxuTBbB7+3I0E5fcuO9yotSBKoQKBgQD6Az8xZmrxq18wZL5T
              Dwh3X5ce/VxoZ9NeDV1AY4DOJdjCo/moIKzPSYTsFSQbrA5qMxeTPpT7T6tGIHFi
              0+ZkPRgToeVEv4CedJ4SxWrVy7qt+TDa3kl19merFFtnkrrDcaDWdA16UXKN+OsK
              kJgHDkpK51roT0Mt0BzjT0YZKwKBgQDgKkHbnGFaaS6HEHCeVCsg2sal5+MyWh68
              fGI6XmMJxSMU9sI6KJobgWDMLNM7kPBeyhH0knL4+CBpQiCbj5GiN9SuunkbxS7C
              VHQImaOVg4JKsan7aMGdrMKXhrp7pFdCqGFAsfC5As2HnxqCHKt0Jy3ivDBw3MKG
              /v6LRFXYDwKBgQCLxYrrtEVvrI5AAOKCjvH9wtw6wAyMdhcuCqyqlbZqCbCgSIoH
              m5ThIkoErmEMc2lvp/azJBaoVUb8oqChTrQMMDeoqicCA0oFzE6fUe1Db22DW8Ab
              bJCHOWWFuJDNW7O8FmeD3Ue3rZcslg/ZTTMhJrUUITmqjL2S/DlaP3SCFwKBgQDR
              eWuVhyrKS+P4vvoeWlT7HLXpLyp2URTL2ggIt08hQCEqY/TYWDy6W0pTQcgar7SP
              P5FL1NavpV7UPcEu3RigWD/dD9BAjSDG5BiSONBO1VeZ6TAJCbl0WI52/qWhCM+q
              MKLAzAdBPEdH5vvkfloR7tLIQEecEA+QCxVemvsFLQKBgQDoxfWf1n2OKdb49QBi
              963yXiU2y3f82h+Pj4u10T7IeDKIbSKP9VDxG3Hs9DQr6CQChKrJ+t8Sy2PFhkNV
              YnQAOd/OFzCJN1NjfZa3z+BfoIdaGU8hZWLAt+RFXpLtKoPI2gHt+BXhlMESmX8U
              f8pO3iWLcEcFoyg1xltkjLLuWw==
              -----END PRIVATE KEY-----
        lifespans:
          access_token: '1h'
          authorize_code: '1m'
          id_token: '1h'
          refresh_token: '90m'
        claims_policies:
          default:
            id_token: ['rat', 'email', 'preferred_username', 'name', 'ext_role', 'ext_mobile', 'ext_address', 'ext_description', 'ext_department']
            custom_claims:
              ext_role:
                attribute: 'role'
              ext_mobile:
                attribute: 'phone_number'
              ext_address:
                attribute: 'street_address'
              ext_description:
                attribute: 'description'
              ext_department:
                attribute: 'department'
        scopes:
          extend:
            claims: ['ext_role', 'ext_mobile', 'ext_address', 'ext_description', 'ext_department']
        clients:
          - client_id: 'example_client_id'
            client_name: 'example_client_name'
            # 【需要修改】42位及以上随机字符串
            client_secret: 'PeqqizzHMN04CigGEgin8kbK6YgKgrkHilGtbaWC63'
            authorization_policy: 'one_factor'
            token_endpoint_auth_method: 'client_secret_post'
            claims_policy: 'default'
            scopes: ['openid', 'profile', 'email', 'extend']
            redirect_uris: ['http://localhost:8080/test/callback', 'http://localhost:5173/cdzmiot/admin/dev-api/oauth2/authelia/callback']
    ​
    # 会话配置
    session:
      # 【需要修改】21位及以上随机字符串
      secret: Li3KiigbUH4xd3BLNotqB
      cookies:
        - name: 'authelia_session'
          # 【需要修改】对应的域名
          domain: 'example.cn'
          # 【需要修改】对应的认证接口路径
          authelia_url: 'https://auth.example.cn'
          expiration: '1 hour'
          inactivity: '5 minutes'
          default_redirection_url: 'https://public.example.cn'
    ​
    # 身份认证后端配置
    authentication_backend:
      password_reset:
        # 是否关闭密码重置
        disable: true
      ldap:
        address: ldap://go-ldap-admin-openldap:389
        implementation: custom
        timeout: 5s
        base_dn: DC=eryajf,DC=net
        additional_users_dn: OU=people
        users_filter: (&({username_attribute}={input})(objectClass=person))
        additional_groups_dn: ''
        groups_filter: (&(uniqueMember={dn})(objectclass=groupOfUniqueNames))
        user: CN=admin,DC=eryajf,DC=net
        password: '123456'
        attributes:
          nickname: givenName
          username: uid
          phone_number: mobile
          street_address: postalAddress
          group_name: businessCategory
          extra:
            departmentNumber:
              name: role
              multi_valued: false
              value_type: string
            description:
              name: description
              multi_valued: false
              value_type: string
            businessCategory:
              name: department
              multi_valued: false
              value_type: string
    ​
    # 存储配置
    storage:
      # 【需要修改】21位及以上随机字符串
      encryption_key: 'nVeh1F9W8vg5jHF3YXYqb'
      # 【需要修改】MYSQL配置
      mysql:
        address: 'tcp://172.18.0.1:3306'
        database: 'YOUR_MYSQL_DATABASE'
        username: 'YOUR_MYSQL_USERNAME'
        password: 'YOUR_MYSQL_PASSWORD'
        timeout: '5s'
    ​
    # 登录尝试规则,当前配置:2分钟最大重试3次,封禁5分钟
    regulation:
      max_retries: 3
      find_time: '2 minutes'
      ban_time: '5 minutes'
    ​
    # 通知
    notifier:
      filesystem:
        filename: '/config/notification.txt'
    ...
    ​

2. docker-compose部署文件

  • ./docker-compose.yml

    networks:
      oidc-network:
        external: true
    services:
    ​
      # OIDC服务
      authelia:
        image: authelia/authelia:4.39.3
        container_name: authelia
        hostname: authelia
        restart: always
        healthcheck:
          disable: true
        environment:
          TZ: Asia/Shanghai
        ports:
          - 39443:9091
        volumes:
          - ./data/authelia/config:/config
        depends_on:
          - openldap
        networks:
          - oidc-network
      
      # ldap管理工具
      go-ldap-admin:
        image: registry.cn-hangzhou.aliyuncs.com/eryajf/go-ldap-admin
        container_name: go-ldap-admin
        hostname: go-ldap-admin
        restart: always
        environment:
          WAIT_HOSTS: openldap:389
          DB_DRIVER: mysql
          # 请修改下面的 MySql 配置,需自行创建数据库和用户
          MYSQL_HOST: YOUR_MYSQL_HOST
          MYSQL_PORT: YOUR_MYSQL_PORT
          MYSQL_USERNAME: YOUR_MYSQL_USERNAME
          MYSQL_PASSWORD: YOUR_MYSQL_PASSWORD
          MYSQL_DATABASE: YOUR_MYSQL_DATABASE
        ports:
          - 39888:8888
        volumes:
          - ./data/go-ldap-admin:/app/data
        depends_on:
          - openldap
        links:
          - openldap:go-ldap-admin-openldap
        networks:
          - oidc-network
    ​
      # ldap服务
      openldap:
        image: registry.cn-hangzhou.aliyuncs.com/eryajf/openldap:1.4.1
        container_name: go-ldap-admin-openldap
        hostname: go-ldap-admin-openldap
        restart: always
        environment:
          TZ: Asia/Shanghai
          LDAP_ORGANISATION: "eryajf.net"
          LDAP_DOMAIN: "eryajf.net"
          LDAP_ADMIN_PASSWORD: "123456"
        command: [ '--copy-service' ]
        volumes:
          - ./data/openldap/database:/var/lib/ldap
          - ./data/openldap/config:/etc/ldap/slapd.d
        ports:
          - 39389:389
        networks:
          - oidc-network
    ​

3. SpringBoot集成

  • pom.xml

    <!-- spring security 安全认证 -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    <!-- spring security 集成 OAuth2 -->
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-oauth2-client</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-oauth2-jose</artifactId>
    </dependency>
  • application.yaml

    spring:
      security:
        oauth2:
          # 与Authelia中的配置保持一致
          client:
            registration:
              sso:
                client-id: example_client_id
                client-secret: PeqqizzHMN04CigGEgin8kbK6YgKgrkHilGtbaWC63
                authorization-grant-type: authorization_code
                redirect-uri: http://localhost:8080/test/callback
                scope: ['openid', 'profile', 'email', 'extend']
            provider:
              sso:
                issuer-uri: https://auth.example.cn
                user-name-attribute: preferred_username
  • SpringSecurityConfig.java

    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
    import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
    ​
    import static org.springframework.security.config.Customizer.withDefaults;
    ​
    @EnableWebSecurity(debug = true)
    public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
    ​
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.formLogin().disable();
            http.csrf().and().cors().disable();
            http.authorizeRequests()
                    .mvcMatchers("/test/callback")
                    .permitAll()
                    .anyRequest().authenticated();
            // 授权码模式回调
            http.oauth2Login(withDefaults());
        }
    ​
    }
  • TestController.java

    import cn.hutool.http.HttpUtil;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    ​
    import java.util.HashMap;
    import java.util.Map;
    ​
    ​
    @RestController
    public class UserController {
    ​
        @Value("${spring.security.oauth2.client.registration.sso.client-id}")
        private String clientId;
    ​
        @Value("${spring.security.oauth2.client.registration.sso.client-secret}")
        private String clientSecret;
    ​
        @Value("${spring.security.oauth2.client.registration.sso.authorization-grant-type}")
        private String grantType;
    ​
        @Value("${spring.security.oauth2.client.registration.sso.redirect-uri}")
        private String redirectUri;
    ​
        @GetMapping("/test/callback")
        public String getTokenByCode(String code) {
            Map<String, Object> paramMap = new HashMap<>();
            paramMap.put("code", code);
            paramMap.put("client_id", clientId);
            paramMap.put("client_secret", clientSecret);
            paramMap.put("grant_type", grantType);
            paramMap.put("redirect_uri", redirectUri);
            return HttpUtil.post("https://auth.example.cn/api/oidc/token", paramMap);
        }
    ​
    }