• PAC4J 初探


    什么是PAC4J?
    pac4j是一个简单而强大的安全引擎,用于Java对用户进行身份验证、获取其配置文件和管理授权,以确保web应用程序安全。它提供了一套完整的概念和组件。它基于Java 8,并在Apache 2许可下使用。它可用于大多数框架/工具和支持大多数认证/授权机制。

    已经集成可用的场景

    • J2E • Spring Web MVC (Spring Boot) • Spring Security (Spring Boot) • Apache Shiro
    • Play 2.x • Vertx • Spark Java • Ratpack • Undertow
    • CAS server • JAX-RS • Dropwizard • Apache Knox • Jooby

    身份验证机制

    • OAuth (Facebook, Twitter, Google…) - SAML - CAS - OpenID Connect - HTTP - OpenID - Google App Engine - Kerberos (SPNEGO/Negotiate)
    • LDAP - SQL - JWT - MongoDB - CouchDB - IP address - REST API

    授权机制

    • Roles/permissions - Anonymous/remember-me/(fully) authenticated - Profile type, attribute
    • CORS - CSRF - Security headers - IP address, HTTP method

    PAC4J基本上基于jdk1.8的环境,由于公司电脑的jdk是1.7,这里就简单的集成CAS就行,其他的认证方式等回家再写,家里的电脑jdk是1.8的。

    • 项目结构

    • pom.xml
    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>org.ikane</groupId>
        <artifactId>demo</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <packaging>jar</packaging>
    
        <name>spring-boot-pac4j-demo</name>
        <description>Spring-boot PAC4J DEMO</description>
    
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>1.2.8.RELEASE</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
    
        <properties>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
            <java.version>1.7</java.version>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-thymeleaf</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.thymeleaf.extras</groupId>
                <artifactId>thymeleaf-extras-springsecurity4</artifactId>
                <version>2.1.2.RELEASE</version>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-security</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.pac4j</groupId>
                <artifactId>spring-security-pac4j</artifactId>
                <version>1.4.1</version>
                <exclusions>
                    <exclusion>
                        <groupId>org.springframework.security</groupId>
                        <artifactId>spring-security-web</artifactId>
                    </exclusion>
                    <exclusion>
                        <groupId>org.springframework.security</groupId>
                        <artifactId>spring-security-config</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
    
            <dependency>
                <groupId>org.pac4j</groupId>
                <artifactId>pac4j-cas</artifactId>
                <version>1.8.5</version>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    
    
    </project>
    • Pac4jConfig.java
    package org.ikane;
    
    import org.ikane.security.ClientUserDetailsService;
    import org.ikane.service.AccountService;
    import org.pac4j.cas.client.CasClient;
    import org.pac4j.core.client.Clients;
    import org.pac4j.springframework.security.authentication.ClientAuthenticationProvider;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    
    @Configuration
    public class Pac4jConfig {
    
        public static String CAS_LOGIN_URL = "https://casserverpac4j.herokuapp.com/login";
    
        @Value("${oauth.callback.url}")
        private String oauthCallbackUrl;
    
        @Autowired
        AccountService accountService;
    
        @Bean
        CasClient casClient() {
            return new CasClient(CAS_LOGIN_URL);
        }
    
        @Bean
        Clients clients() {
            return new Clients(oauthCallbackUrl, casClient());
        }
    
    
    
        @Bean
        ClientUserDetailsService clientUserDetailsService() {
            ClientUserDetailsService clientUserDetailsService = new ClientUserDetailsService();
            clientUserDetailsService.setAccountService(accountService);
            return clientUserDetailsService;
        }
    
        @Bean
        ClientAuthenticationProvider clientProvider() {
            ClientAuthenticationProvider clientAuthenticationProvider = new ClientAuthenticationProvider();
            clientAuthenticationProvider.setClients(clients());
            clientAuthenticationProvider.setUserDetailsService(clientUserDetailsService());
            return clientAuthenticationProvider;
        }
    
    }
    • SecurityConfig.java
    package org.ikane;
    
    import org.pac4j.core.client.Clients;
    import org.pac4j.springframework.security.authentication.ClientAuthenticationProvider;
    import org.pac4j.springframework.security.web.ClientAuthenticationFilter;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.security.authentication.AuthenticationManager;
    import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
    import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.config.annotation.web.builders.WebSecurity;
    import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
    import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
    import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
    import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
    import org.springframework.security.web.authentication.session.SessionFixationProtectionStrategy;
    
    @Configuration
    @EnableWebSecurity
    @EnableGlobalMethodSecurity(prePostEnabled=true, securedEnabled=true)
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
        @Autowired
        ApplicationContext context;
    
        @Autowired
        Clients clients;
    
        @Autowired
        ClientAuthenticationProvider clientProvider;
    
        @Override
        public void configure(WebSecurity web) throws Exception {
             web
             .ignoring()
             .antMatchers(
                 "/**/*.css",
                 "/**/*.png",
                 "/**/*.gif",
                 "/**/*.jpg",
                 "/**/*.ico",
                 "/**/*.js"
             );
        }
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http
            .csrf().disable()
            .authorizeRequests()
                .and()
            .formLogin()
                .loginPage("/login")
                .permitAll()
                .and()
            .logout()
                .logoutUrl("/logout")
                .logoutSuccessUrl("/")
                .permitAll()
             ;
    
            http.addFilterBefore(clientFilter(), UsernamePasswordAuthenticationFilter.class);
        }
    
        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            super.configure(auth);
    
            auth.authenticationProvider(clientProvider);
        }
    
        ClientAuthenticationFilter clientFilter() {
    
            String suffixUrl="/*";
    
            ClientAuthenticationFilter clientAuthenticationFilter = new ClientAuthenticationFilter(suffixUrl);
            clientAuthenticationFilter.setClients(clients);
            clientAuthenticationFilter.setSessionAuthenticationStrategy(sas());
            //clientAuthenticationFilter.setAuthenticationManager((AuthenticationManager)clientProvider);
    
            return clientAuthenticationFilter;
            /*
            return new ClientAuthenticationFilter(
                    clients: clients,
                    sessionAuthenticationStrategy: sas(),
                    authenticationManager: clientProvider as AuthenticationManager
            )
            */
        }
    
        @Bean
        SessionAuthenticationStrategy sas() {
            return new SessionFixationProtectionStrategy();
        }
    
    }
    • SpringBootPac4jDemoApplication.java
    package org.ikane;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    
    @SpringBootApplication
    public class SpringBootPac4jDemoApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(SpringBootPac4jDemoApplication.class, args);
        }
    }
    • ThymeleafConfig.java
    package org.ikane;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.thymeleaf.extras.springsecurity4.dialect.SpringSecurityDialect;
    
    @Configuration
    public class ThymeleafConfig {
    
        @Bean
        public SpringSecurityDialect springSecurityDialect() {
            return new SpringSecurityDialect();
        }
    }
    • IndexController.java
    package org.ikane.controller;
    
    import org.springframework.security.access.prepost.PreAuthorize;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    @Controller
    class IndexController {
    
        @RequestMapping("/")
        @PreAuthorize("isAuthenticated()")
        public String index() {
            return "index";
        }
    
    }
    • LoginController.java
    package org.ikane.controller;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import org.pac4j.cas.client.CasClient;
    import org.pac4j.core.client.BaseClient;
    import org.pac4j.core.client.Clients;
    import org.pac4j.core.context.J2EContext;
    import org.pac4j.core.context.WebContext;
    import org.pac4j.core.exception.RequiresHttpAction;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.security.authentication.AnonymousAuthenticationToken;
    import org.springframework.security.core.Authentication;
    import org.springframework.security.core.context.SecurityContextHolder;
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    @Controller
    public class LoginController {
    
        Logger logger = LoggerFactory.getLogger(LoginController.class);
    
        @Autowired
        private Clients clients;
    
        @RequestMapping("/login")
        public String login(HttpServletRequest request, HttpServletResponse response, Model model) {
    
            if (isAuthenticated()) {
                return "redirect:/";
            }
            final WebContext context = new J2EContext(request, response);
            //定义cas客户端
            final CasClient casClient = (CasClient) clients.findClient(CasClient.class);
            model.addAttribute("casAuthUrl",  getClientLocation(casClient, context));
            return "login";
        }
    
        //获取客户端的链接
        public String getClientLocation(BaseClient client, WebContext context) {
    
            try {
                return ((CasClient)client).getRedirectAction(context, false).getLocation();
            } catch (RequiresHttpAction e) {
                e.printStackTrace();
                logger.error("error", e);
                return null;
            }
    
            //return client.getRedirectAction(context, false, false).getLocation();
        }
    
    
        protected boolean isAuthenticated() {
            Authentication auth = SecurityContextHolder.getContext().getAuthentication();
            return !(auth instanceof AnonymousAuthenticationToken);
        }
    }
    • ClientUserDetails.java
    package org.ikane.security;
    
    import java.util.Collection;
    
    import org.springframework.security.core.GrantedAuthority;
    import org.springframework.security.core.userdetails.UserDetails;
    
    class ClientUserDetails implements UserDetails {
    
        private static final long serialVersionUID = 6523314653561682296L;
    
        String username;
        String providerId;
        Collection<GrantedAuthority> authorities;
        String password;
    
        public ClientUserDetails() {
            // TODO Auto-generated constructor stub
        }
    
        public ClientUserDetails(String username, String providerId, Collection<GrantedAuthority> authorities) {
            super();
            this.username = username;
            this.providerId = providerId;
            this.authorities = authorities;
        }
    
    
        @Override
        public Collection<? extends GrantedAuthority> getAuthorities() {
            return authorities;
        }
        @Override
        public String getPassword() {
            return password;
        }
        @Override
        public String getUsername() {
            // TODO Auto-generated method stub
            return username;
        }
        @Override
        public boolean isAccountNonExpired() {
            return true;
        }
        @Override
        public boolean isAccountNonLocked() {
            return false;
        }
        @Override
        public boolean isCredentialsNonExpired() {
            return true;
        }
        @Override
        public boolean isEnabled() {
            return true;
        }
        public String getProviderId() {
            return providerId;
        }
        public void setProviderId(String providerId) {
            this.providerId = providerId;
        }
        public void setUsername(String username) {
            this.username = username;
        }
        public void setAuthorities(Collection<GrantedAuthority> authorities) {
            this.authorities = authorities;
        }
        public void setPassword(String password) {
            this.password = password;
        }
    
    
    
    }
    • ClientUserDetailsService.java
    package org.ikane.security;
    
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Map;
    
    import org.ikane.service.AccountService;
    import org.pac4j.springframework.security.authentication.ClientAuthenticationToken;
    import org.springframework.security.core.GrantedAuthority;
    import org.springframework.security.core.authority.SimpleGrantedAuthority;
    import org.springframework.security.core.userdetails.AuthenticationUserDetailsService;
    import org.springframework.security.core.userdetails.UserDetails;
    import org.springframework.security.core.userdetails.UsernameNotFoundException;
    
    public class ClientUserDetailsService implements AuthenticationUserDetailsService<ClientAuthenticationToken> {
    
        private AccountService accountService;
    
        public UserDetails loadUserDetails(final ClientAuthenticationToken token) throws UsernameNotFoundException {
    
            Map account = accountService.lookupAccountByProvider(token.getClientName(), token.getUserProfile().getId());
    
            //String username = account.containsKey("displayName") ? account.displayName : ""
            String username = "admin";
    
            final List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
            for (String role: token.getUserProfile().getRoles()) {
                authorities.add(new SimpleGrantedAuthority(role));
            }
    
            if (!account.isEmpty() && authorities.isEmpty()) {
                // default to user role
                authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
            }
    
            return new ClientUserDetails(username, token.getUserProfile().getId(), authorities);
        }
    
        public AccountService getAccountService() {
            return accountService;
        }
    
        public void setAccountService(AccountService accountService) {
            this.accountService = accountService;
        }
    
    }
    • AccountService.java
    package org.ikane.service;
    
    import java.util.HashMap;
    import java.util.Map;
    
    import org.springframework.stereotype.Service;
    
    @Service
    public class AccountService {
    
    
        /**
         * @Autowired
        JdbcTemplate jdbcTemplate
         * */
    
        public Map lookupAccountByProvider(String providerName, String providerUserId) {
            HashMap<Object,Object> map = new HashMap<>();
    
            /**
             *  
             List results = jdbcTemplate.query(
                    "select * from account where provider = ? and provider_user_id = ?",
                    [providerName, providerUserId] as Object[],
                    new GenericRowMapper()
            )
    
            if (results.size() > 1) {
                throw new Exception("multiple accounts by provider [${providerName}] for id [${providerUserId}]")
            }
    
             * **/
    
            return map;
        }
    
        public Boolean createAccountForProvider(String providerName, String providerUserId, String displayName) {
    
            /**
             *  log.debug("creating new account for displayName=${displayName} using provider=${providerName} with id ${providerUserId}")
    
            int result = jdbcTemplate.update(
                    "insert into account (display_name, provider, provider_user_id) values (?, ?, ?)",
                    displayName,
                    providerName,
                    providerUserId
            )
    
            if (result != 1) {
                log.warn("creation of account for provider [${providerName}] and id [${providerUserId}] failed")
                return false
            }
             * */
            return true;
        }
    }
    • index.html
    <!doctype html>
    <html xmlns:th="http://www.thymeleaf.org">
        <head>
            <title>Spring Pac4j Demo</title>
        </head>
        <body>
            <h2>Index Page sgdsfg</h2>
        </body>
    </html>
    • login.html
    <!doctype html>
    <html xmlns:th="http://www.thymeleaf.org"
          xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
          layout:decorator="layouts/default">
        <head>
            <title>Login</title>
        </head>
        <body>
            <div id="content" class="sign-in-page" layout:fragment="content">
                <h2>Sign In</h2>
                <a th:href="${casAuthUrl}" th:class="'oauth-login-link cas-login'">CAS</a>
                <a th:href="${gitHubAuthUrl}" th:class="'oauth-login-link github-login'">GitHub</a>
                <a th:href="${google2AuthUrl}" th:class="'oauth-login-link google-login'">Google</a>
                <a th:href="${twitterAuthUrl}" th:class="'oauth-login-link twitter-login'">Twitter</a>
            </div>
        </body>
    </html>
    • run

    • index

    本文转载自:https://blog.csdn.net/change_on/article/details/76302161

  • 相关阅读:
    [1] Tornado Todo Day0
    [0] Tornado Todo 开篇
    RNSS和RDSS
    国密随机数检测--2/15 块内频数检测
    国密随机数检测--1/15 单比特频数检测
    FPGA实用通信协议之IIC
    压缩感知(十)
    压缩感知(九)
    压缩感知(八)
    压缩感知(七)
  • 原文地址:https://www.cnblogs.com/wpcnblog/p/15219139.html
Copyright © 2020-2023  润新知