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阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,490评论 18 139
  • Spring Boot 参考指南 介绍 转载自:https://www.gitbook.com/book/qbgb...
    毛宇鹏阅读 46,678评论 6 342
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,493评论 18 399
  • 这是离开临汾最后一次去师大拍的毓秀湖!看到了要搬新校区的消息,心中却没有一丝兴奋,以后的师大还是我回忆中的师大吗?...
    尼古拉斯FY阅读 423评论 0 6
  • 153 幼儿教师,即小孩子的老师,职业道德 ,即职业的道德 是道德在职业活动中的一种特殊要求和准则,是道德的组成...
    涅磐新生阅读 621评论 0 0