springboot+cas單點登錄

1.創建工程
? ? ? 創建Maven工程:springboot-security-cas
2.加入依賴
? ? ? 創建工程后,打開pom.xml,在pom.xml中加入以下內容:

<parent>  
        <groupId>org.springframework.boot</groupId>  
        <artifactId>spring-boot-starter-parent</artifactId>  
        <version>1.4.3.RELEASE</version>  
    </parent>  
    <properties>  
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>  
        <java.version>1.8</java.version>  
    </properties>  
    <dependencies>  
        <dependency>  
            <groupId>org.springframework.boot</groupId>  
            <artifactId>spring-boot-starter</artifactId>  
        </dependency>  
        <dependency>  
            <groupId>org.springframework.boot</groupId>  
            <artifactId>spring-boot-starter-web</artifactId>  
        </dependency>  
        <!-- security starter Poms -->  
        <dependency>  
            <groupId>org.springframework.boot</groupId>  
            <artifactId>spring-boot-starter-security</artifactId>  
        </dependency>  
        <!-- security 對CAS支持 -->  
        <dependency>  
            <groupId>org.springframework.security</groupId>  
            <artifactId>spring-security-cas</artifactId>  
        </dependency>  
        <!-- security taglibs -->  
        <dependency>  
            <groupId>org.springframework.security</groupId>  
            <artifactId>spring-security-taglibs</artifactId>  
        </dependency>  
        <!-- 熱加載 -->  
        <dependency>  
            <groupId>org.springframework.boot</groupId>  
            <artifactId>spring-boot-devtools</artifactId>  
            <optional>true</optional>  
        </dependency>  
        <dependency>  
            <groupId>org.springframework.boot</groupId>  
            <artifactId>spring-boot-configuration-processor</artifactId>  
            <optional>true</optional>  
        </dependency>  
    </dependencies>  
    <build>  
        <plugins>  
            <plugin>  
                <groupId>org.springframework.boot</groupId>  
                <artifactId>spring-boot-maven-plugin</artifactId>  
            </plugin>  
        </plugins>  
    </build>  

3.創建application.properties
創建application.properties文件,加入以下內容:

#CAS服務地址  
cas.server.host.url=http://localhost:8081/cas  
#CAS服務登錄地址  
cas.server.host.login_url=${cas.server.host.url}/login  
#CAS服務登出地址  
cas.server.host.logout_url=${cas.server.host.url}/logout?service=${app.server.host.url}  
#應用訪問地址  
app.server.host.url=http://localhost:8080  
#應用登錄地址  
app.login.url=/login  
#應用登出地址  
app.logout.url=/logout  

4.創建入口啟動類(MainConfig)
創建入口啟動類MainConfig,完整代碼如下:

package com.lidd.springboot;  
  
import org.springframework.boot.SpringApplication;  
import org.springframework.boot.autoconfigure.SpringBootApplication;  
import org.springframework.security.access.prepost.PreAuthorize;  
import org.springframework.web.bind.annotation.RequestMapping;  
import org.springframework.web.bind.annotation.RestController;  
  
@RestController  
@SpringBootApplication  
public class MainConfig {  
    public static void main(String[] args) {  
        SpringApplication.run(MainConfig.class, args);  
    }  
  
    @RequestMapping("/")  
    public String index() {  
        return "訪問了首頁哦";  
    }  
  
    @RequestMapping("/hello")  
    public String hello() {  
        return "不驗證哦";  
    }  
  
    @PreAuthorize("hasAuthority('TEST')")//有TEST權限的才能訪問  
    @RequestMapping("/security")  
    public String security() {  
        return "hello world security";  
    }  
  
    @PreAuthorize("hasAuthority('ADMIN')")//必須要有ADMIN權限的才能訪問  
    @RequestMapping("/authorize")  
    public String authorize() {  
        return "有權限訪問";  
    }  
      
    /**這里注意的是,TEST與ADMIN只是權限編碼,可以自己定義一套規則,根據實際情況即可*/  
}  

5.創建Security配置類(SecurityConfig)
創建Security配置類SecurityConfig,完整代碼如下:

package com.lidd.springboot.security;  
  
import org.jasig.cas.client.session.SingleSignOutFilter;  
import org.jasig.cas.client.validation.Cas20ServiceTicketValidator;  
import org.springframework.beans.factory.annotation.Autowired;  
import org.springframework.context.annotation.Bean;  
import org.springframework.context.annotation.Configuration;  
import org.springframework.security.cas.ServiceProperties;  
import org.springframework.security.cas.authentication.CasAssertionAuthenticationToken;  
import org.springframework.security.cas.authentication.CasAuthenticationProvider;  
import org.springframework.security.cas.web.CasAuthenticationEntryPoint;  
import org.springframework.security.cas.web.CasAuthenticationFilter;  
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.configuration.EnableWebSecurity;  
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;  
import org.springframework.security.core.userdetails.AuthenticationUserDetailsService;  
import org.springframework.security.web.authentication.logout.LogoutFilter;  
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;  
  
import com.lidd.springboot.custom.CustomUserDetailsService;  
import com.lidd.springboot.properties.CasProperties;  
  
@Configuration  
@EnableWebSecurity //啟用web權限  
@EnableGlobalMethodSecurity(prePostEnabled = true) //啟用方法驗證  
public class SecurityConfig extends WebSecurityConfigurerAdapter {  
    @Autowired  
    private CasProperties casProperties;  
      
    /**定義認證用戶信息獲取來源,密碼校驗規則等*/  
    @Override  
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {  
        super.configure(auth);  
        auth.authenticationProvider(casAuthenticationProvider());  
        //inMemoryAuthentication 從內存中獲取  
        //auth.inMemoryAuthentication().withUser("lidd").password("123456").roles("USER")  
        //.and().withUser("admin").password("123456").roles("ADMIN");  
          
        //jdbcAuthentication從數據庫中獲取,但是默認是以security提供的表結構  
        //usersByUsernameQuery 指定查詢用戶SQL  
        //authoritiesByUsernameQuery 指定查詢權限SQL  
        //auth.jdbcAuthentication().dataSource(dataSource).usersByUsernameQuery(query).authoritiesByUsernameQuery(query);  
          
        //注入userDetailsService,需要實現userDetailsService接口  
        //auth.userDetailsService(userDetailsService);  
    }  
      
    /**定義安全策略*/  
    @Override  
    protected void configure(HttpSecurity http) throws Exception {  
        http.authorizeRequests()//配置安全策略  
            //.antMatchers("/","/hello").permitAll()//定義/請求不需要驗證  
            .anyRequest().authenticated()//其余的所有請求都需要驗證  
            .and()  
        .logout()  
            .permitAll()//定義logout不需要驗證  
            .and()  
        .formLogin();//使用form表單登錄  
          
        http.exceptionHandling().authenticationEntryPoint(casAuthenticationEntryPoint())  
            .and()  
            .addFilter(casAuthenticationFilter())  
            .addFilterBefore(casLogoutFilter(), LogoutFilter.class)  
            .addFilterBefore(singleSignOutFilter(), CasAuthenticationFilter.class);  
          
        //http.csrf().disable(); //禁用CSRF  
    }  
      
    /**認證的入口*/  
    @Bean  
    public CasAuthenticationEntryPoint casAuthenticationEntryPoint() {  
        CasAuthenticationEntryPoint casAuthenticationEntryPoint = new CasAuthenticationEntryPoint();  
        casAuthenticationEntryPoint.setLoginUrl(casProperties.getCasServerLoginUrl());  
        casAuthenticationEntryPoint.setServiceProperties(serviceProperties());  
        return casAuthenticationEntryPoint;  
    }  
      
    /**指定service相關信息*/  
    @Bean  
    public ServiceProperties serviceProperties() {  
        ServiceProperties serviceProperties = new ServiceProperties();  
        serviceProperties.setService(casProperties.getAppServerUrl() + casProperties.getAppLoginUrl());  
        serviceProperties.setAuthenticateAllArtifacts(true);  
        return serviceProperties;  
    }  
      
    /**CAS認證過濾器*/  
    @Bean  
    public CasAuthenticationFilter casAuthenticationFilter() throws Exception {  
        CasAuthenticationFilter casAuthenticationFilter = new CasAuthenticationFilter();  
        casAuthenticationFilter.setAuthenticationManager(authenticationManager());  
        casAuthenticationFilter.setFilterProcessesUrl(casProperties.getAppLoginUrl());  
        return casAuthenticationFilter;  
    }  
      
    /**cas 認證 Provider*/  
    @Bean  
    public CasAuthenticationProvider casAuthenticationProvider() {  
        CasAuthenticationProvider casAuthenticationProvider = new CasAuthenticationProvider();  
        casAuthenticationProvider.setAuthenticationUserDetailsService(customUserDetailsService());  
        //casAuthenticationProvider.setUserDetailsService(customUserDetailsService()); //這里只是接口類型,實現的接口不一樣,都可以的。  
        casAuthenticationProvider.setServiceProperties(serviceProperties());  
        casAuthenticationProvider.setTicketValidator(cas20ServiceTicketValidator());  
        casAuthenticationProvider.setKey("casAuthenticationProviderKey");  
        return casAuthenticationProvider;  
    }  
      
    /*@Bean 
    public UserDetailsService customUserDetailsService(){ 
        return new CustomUserDetailsService(); 
    }*/  
      
    /**用戶自定義的AuthenticationUserDetailsService*/  
    @Bean  
    public AuthenticationUserDetailsService<CasAssertionAuthenticationToken> customUserDetailsService(){  
        return new CustomUserDetailsService();  
    }  
      
    @Bean  
    public Cas20ServiceTicketValidator cas20ServiceTicketValidator() {  
        return new Cas20ServiceTicketValidator(casProperties.getCasServerUrl());  
    }  
      
    /**單點登出過濾器*/  
    @Bean  
    public SingleSignOutFilter singleSignOutFilter() {  
        SingleSignOutFilter singleSignOutFilter = new SingleSignOutFilter();  
        singleSignOutFilter.setCasServerUrlPrefix(casProperties.getCasServerUrl());  
        singleSignOutFilter.setIgnoreInitConfiguration(true);  
        return singleSignOutFilter;  
    }  
      
    /**請求單點退出過濾器*/  
    @Bean  
    public LogoutFilter casLogoutFilter() {  
        LogoutFilter logoutFilter = new LogoutFilter(casProperties.getCasServerLogoutUrl(), new SecurityContextLogoutHandler());  
        logoutFilter.setFilterProcessesUrl(casProperties.getAppLogoutUrl());  
        return logoutFilter;  
    }  
}  

6.用戶自定義類
(1)定義CasProperties,用于將properties文件指定的內容注入以方便使用,這里不注入也是可以的,可以獲取Spring 當前的環境,代碼如下:

package com.lidd.springboot.properties;  
  
import org.springframework.beans.factory.annotation.Value;  
import org.springframework.stereotype.Component;  
  
/** 
 * CAS的配置參數 
 * @author lidd 
 */  
@Component  
public class CasProperties {  
    @Value("${cas.server.host.url}")  
    private String casServerUrl;  
  
    @Value("${cas.server.host.login_url}")  
    private String casServerLoginUrl;  
  
    @Value("${cas.server.host.logout_url}")  
    private String casServerLogoutUrl;  
  
    @Value("${app.server.host.url}")  
    private String appServerUrl;  
  
    @Value("${app.login.url}")  
    private String appLoginUrl;  
  
    @Value("${app.logout.url}")  
    private String appLogoutUrl;  
......省略 getters setters 方法  
}  

(2)定義CustomUserDetailsService類,代碼如下:

package com.lidd.springboot.custom;  
  
import java.util.HashSet;  
import java.util.Set;  
  
import org.springframework.security.cas.authentication.CasAssertionAuthenticationToken;  
import org.springframework.security.core.userdetails.AuthenticationUserDetailsService;  
import org.springframework.security.core.userdetails.UserDetails;  
import org.springframework.security.core.userdetails.UsernameNotFoundException;  
  
/** 
 * 用于加載用戶信息 實現UserDetailsService接口,或者實現AuthenticationUserDetailsService接口 
 * @author lidd 
 * 
 */  
public class CustomUserDetailsService /* 
    //實現UserDetailsService接口,實現loadUserByUsername方法 
    implements UserDetailsService { 
    @Override 
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { 
        System.out.println("當前的用戶名是:"+username); 
        //這里我為了方便,就直接返回一個用戶信息,實際當中這里修改為查詢數據庫或者調用服務什么的來獲取用戶信息 
        UserInfo userInfo = new UserInfo(); 
        userInfo.setUsername("admin"); 
        userInfo.setName("admin"); 
        Set<AuthorityInfo> authorities = new HashSet<AuthorityInfo>(); 
        AuthorityInfo authorityInfo = new AuthorityInfo("TEST"); 
        authorities.add(authorityInfo); 
        userInfo.setAuthorities(authorities); 
        return userInfo; 
    }*/  
      
      
    //實現AuthenticationUserDetailsService,實現loadUserDetails方法  
    implements AuthenticationUserDetailsService<CasAssertionAuthenticationToken> {  
  
    @Override  
    public UserDetails loadUserDetails(CasAssertionAuthenticationToken token) throws UsernameNotFoundException {  
        System.out.println("當前的用戶名是:"+token.getName());  
        /*這里我為了方便,就直接返回一個用戶信息,實際當中這里修改為查詢數據庫或者調用服務什么的來獲取用戶信息*/  
        UserInfo userInfo = new UserInfo();  
        userInfo.setUsername("admin");  
        userInfo.setName("admin");  
        Set<AuthorityInfo> authorities = new HashSet<AuthorityInfo>();  
        AuthorityInfo authorityInfo = new AuthorityInfo("TEST");  
        authorities.add(authorityInfo);  
        userInfo.setAuthorities(authorities);  
        return userInfo;  
    }  
  
}  

(3)定義AuthorityInfo類,用于加載當前登錄用戶的權限信息,實現GrantedAuthority接口,代碼如下:

package com.lidd.springboot.custom;  
  
import org.springframework.security.core.GrantedAuthority;  
  
/** 
 * 權限信息 
 *  
 * @author lidd 
 * 
 */  
public class AuthorityInfo implements GrantedAuthority {  
    private static final long serialVersionUID = -175781100474818800L;  
  
    /** 
     * 權限CODE 
     */  
    private String authority;  
  
    public AuthorityInfo(String authority) {  
        this.authority = authority;  
    }  
  
    @Override  
    public String getAuthority() {  
        return authority;  
    }  
  
    public void setAuthority(String authority) {  
        this.authority = authority;  
    }  
  
}  

(4)定義UserInfo類,用于加載當前用戶信息,實現UserDetails接口,代碼如下:

package com.lidd.springboot.custom;  
  
import java.util.Collection;  
import java.util.HashSet;  
import java.util.Set;  
  
import org.springframework.security.core.GrantedAuthority;  
import org.springframework.security.core.userdetails.UserDetails;  
  
/** 
 * 用戶信息 
 * @、這里我寫了幾個較為常用的字段,id,name,username,password,可以根據實際的情況自己增加 
 * @author lidd 
 * 
 */  
public class UserInfo implements UserDetails {  
    private static final long serialVersionUID = -1041327031937199938L;  
  
    /** 
     * 用戶ID 
     */  
    private Long id;  
  
    /** 
     * 用戶名稱 
     */  
    private String name;  
  
    /** 
     * 登錄名稱 
     */  
    private String username;  
  
    /** 
     * 登錄密碼 
     */  
    private String password;  
  
    private boolean isAccountNonExpired = true;  
  
    private boolean isAccountNonLocked = true;  
  
    private boolean isCredentialsNonExpired = true;  
  
    private boolean isEnabled = true;  
  
    private Set<AuthorityInfo> authorities = new HashSet<AuthorityInfo>();  
....省略getters setters 方法  
}  
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,321評論 6 543
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,559評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,442評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,835評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,581評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,922評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,931評論 3 447
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,096評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,639評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,374評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,591評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,104評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,789評論 3 349
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,196評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,524評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,322評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,554評論 2 379

推薦閱讀更多精彩內容

  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,828評論 18 139
  • Spring Boot 參考指南 介紹 轉載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 46,922評論 6 342
  • 1. Java基礎部分 基礎部分的順序:基本語法,類相關的語法,內部類的語法,繼承相關的語法,異常的語法,線程的語...
    子非魚_t_閱讀 31,737評論 18 399
  • 這是離開臨汾最后一次去師大拍的毓秀湖!看到了要搬新校區的消息,心中卻沒有一絲興奮,以后的師大還是我回憶中的師大嗎?...
    尼古拉斯FY閱讀 446評論 0 6
  • 153 幼兒教師,即小孩子的老師,職業道德 ,即職業的道德 是道德在職業活動中的一種特殊要求和準則,是道德的組成...
    涅磐新生閱讀 666評論 0 0