Skip to content

CAS

1.下载安装Cas

由于6.0以上的版本需要JDK11,所以这里下载5.3(maven3.3/jdk8)

https://github.com/apereo/cas-overlay-template/tree/5.3

  1. 下载后导入到Idea中,右键-git-branchs 切换到5.3
  2. 命令行输入

    mvn clean package

  3. 执行完毕之后在target目录下找到cas.war
  4. 用Tomcat运行这个war,浏览器访问

    http://localhost:8080/cas/login

  5. application.properties中修改账号密码,默认:casuser/Mellon
  6. 由于CAS默认使用的是基于https协议,需要改为兼容使用http协议,并修改端口为8080
properties
cas.authn.accept.users=root::123123
#兼容 Http 协议
cas.tgc.secure=false
# 开启识别Json文件,默认false
cas.serviceRegistry.initFromJson=true
# 允许退出登录后重定向到其他页面
cas.logout.followServiceRedirects=true
  1. 默认的Https添加http cas\WEB-INF\classes\services目录下的HTTPSandIMAPS-10000001.json 修改内容如下,即添加http

"serviceId" : "^(https|http|imaps)😕/.*",

2.客户端1:8088

  1. 依赖
xml

<dependency>
    <!--第三方starter,可免去配置类,不过它没有配置登出,可手动注册登出bean-->
    <groupId>net.unicon.cas</groupId>
    <artifactId>cas-client-autoconfig-support</artifactId>
    <version>2.1.0-GA</version>
</dependency>
  1. 配置
server.port=8088
#cas服务端的地址
cas.server-url-prefix=http://localhost:8080/cas
#cas服务端的登录地址
cas.server-login-url=http://localhost:8080/cas/login
#当前服务器的地址(客户端)
cas.client-host-url=http://localhost:8088
#Ticket校验器使用Cas30ProxyReceivingTicketValidationFilter
cas.validation-type=cas3
import net.unicon.cas.client.configuration.EnableCasClient;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@EnableCasClient//开启cas客户端服务
@SpringBootApplication
public class CasApplication {
    public static void main(String[] args) {
        SpringApplication.run(CasApplication.class, args);
    }

    @Value(value = "${cas.client-host-url}")
    private String server_name ;
    @Value(value = "${cas.server-login-url}")
    private String login_url ;


    @Bean//登出用
    public FilterRegistrationBean filterAuthenticationRegistration() {
        FilterRegistrationBean registration = new FilterRegistrationBean();
        // AuthenticationFilter  该过滤器负责用户的认证工作
        registration.setFilter(new AuthenticationFilter());
        // 设定匹配的路径
        registration.addUrlPatterns("/*");
        Map<String,String> initParameters = new HashMap<String, String>();
        initParameters.put("casServerLoginUrl", login_url);
        initParameters.put("serverName", server_name);
        // 忽略 /logoutSuccess 的路径
        initParameters.put("ignorePattern", "/logoutSuccess/*");
        registration.setInitParameters(initParameters);
        // 设定加载的顺序
        registration.setOrder(1);
        return registration;
    }

}
  1. 测试接口
java

@RequestMapping("/test1")
public String test1() {
    return "test1....";
}

@RequestMapping("/logout")
public String logout(HttpServletRequest request) {
    HttpSession session = request.getSession();
    session.invalidate();
    return "redirect:http://localhost:8080/cas/logout?service=http://localhost:8088/logoutSuccess";
}

@RequestMapping("/logoutSuccess")
@ResponseBody
public String logoutSuccess() {
    return "member logoutSuccess";
}

浏览器访问test1接口自动跳转到cas的登录页面,输入密码后返回结果,浏览器访问logout接口,返回结果logoutSuccess

3.客户端2:8089

除了其端口不一样以外,其余均一致

启动两个客户端,访问登录任一接口,另外一个几口便不需要登录;

登出同理.

4.cas配置JDBC

  1. 在源码5.3的pom中添加以下依赖,并重新打war包
xml

<dependencies>
    <!-- Database Authentication Begin -->
    <dependency>
        <groupId>org.apereo.cas</groupId>
        <artifactId>cas-server-support-jdbc</artifactId>
        <version>${cas.version}</version>
    </dependency>
    <!--自适配数据库驱动,其中包括HSQLDB、Oracle、MYSQL(8)、PostgreSQL、MariaDB、Microsoft SQL Server-->
    <dependency>
        <groupId>org.apereo.cas</groupId>
        <artifactId>cas-server-support-jdbc-drivers</artifactId>
        <version>${cas.version}</version>
    </dependency>
    <!-- Database Authentication End -->
</dependencies>
  1. 重新打包,并修改application文件
properties
# 兼容 Http 协议
cas.tgc.secure=false
# 开启识别Json文件,默认false
cas.serviceRegistry.initFromJson=true
# 允许退出登录后重定向到其他页面
cas.logout.followServiceRedirects=true
##
# CAS Authentication Credentials
#
#cas.authn.accept.users=casuser::Mellon
#jdbc验证配置
#Query Database Authentication 数据库查询校验用户名开始
#查询账号密码sql,必须包含密码字段
cas.authn.jdbc.query[0].sql=select * from user where name=? 
#指定上面的sql查询字段名(必须)
cas.authn.jdbc.query[0].fieldPassword=password
#指定过期字段,1为过期,若过期需要修改密码
#cas.authn.jdbc.query[0].fieldExpired=expired
#为不可用字段段,1为不可用,
#cas.authn.jdbc.query[0].fieldDisabled=disabled
#数据库方言hibernate的知识
cas.authn.jdbc.query[0].dialect=org.hibernate.dialect.MySQLDialect
#数据库驱动
cas.authn.jdbc.query[0].driverClass=com.mysql.cj.jdbc.Driver
#数据库连接
cas.authn.jdbc.query[0].url=jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
#数据库用户名
cas.authn.jdbc.query[0].user=root
#数据库密码
cas.authn.jdbc.query[0].password=123456
#默认加密策略,通过encodingAlgorithm来指定算法,默认NONE不加密
#cas.authn.jdbc.query[0].passwordEncoder.type=com.sso.config.MyPasswordEncoder
cas.authn.jdbc.query[0].passwordEncoder.characterEncoding=UTF-8
#cas.authn.jdbc.query[0].passwordEncoder.encodingAlgorithm=MD5
#Query Database Authentication 数据库查询校验用户名结束
#jdbc验证配置
  1. 启动客户端,即可.

Oauth2

1.简介

OAuth是一个关于授权(authorization)的开放网络标准,

oauth2根据使用场景不同,分成了4种模式

  • 授权码模式(authorization code),最复杂最常用的模式

第一步:【A服务客户端】将用户自动导航到【B服务认证服务】,这一步用户需要提供一个回调地址,以备【B服务认证服务】返回授权码使用。 第二步:用户点击授权按钮表示让【A服务客户端】使用【B服务资源服务】,这一步需要用户登录B服务,也就是说用户要事先具有B服务的使用权限。 第三步:【B服务认证服务】生成授权码,授权码将通过第一步提供的回调地址,返回给【A服务客户端】。( 注意这个授权码并非通行【B服务资源服务】的通行凭证。)

第四步:【A服务认证服务】携带上一步得到的授权码向【B服务认证服务】发送请求,获取通行凭证token第五步 :【B服务认证服务】给【A服务认证服务】返回令牌token和更新令牌refresh token

  • 简化模式(implicit)

第一步:【A服务客户端】将用户自动导航到【B服务认证服务】,这一步用户需要提供一个回调地址,以备【B服务认证服务】返回token 使用,还会携带一个【A服务客户端】的状态标识state第二步 :用户点击授权按钮表示让【A服务客户端】使用【B服务资源服务】,这一步需要用户登录B服务,也就是说用户要事先具有B服务的使用权限。

第三步:【 B服务认证服务】生成通行令牌tokentoken将通过第一步提供的回调地址,返回给【A服务客户端】。

  • 密码模式(resource owner password credentials)

第一步:直接告诉【A服务客户端】自己的【B服务认证服务】的用户名和密码 第二步 :【A服务客户端】携带【B服务认证服务】的用户名和密码向【B服务认证服务】发起请求获取 token第三步 :【B服务认证服务】给【A服务客户端】颁发token

  • 客户端模式(client credentials)

第一步:A服务向B服务索取token第二步:B服务返回token给A服务。

待续....

SpringSecurity

1.简介

重要的类(SpringSecurity的核心)

  • WebSecurityConfigureAdapter: 自定义Security策略
  • AuthenticationManagerBuilder: 自定义认证策略
  • @EnableWebSecurity: 开启WebSecurity模式

两个核心目标是"认证"和"授权" (访问控制)

认证 ====> Authentication

授权 ====> Authorization

2.依赖

xml
<!--security-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

3.配置类

extends WebSecurityConfigurerAdapter

java
package zong.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
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.crypto.bcrypt.BCryptPasswordEncoder;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {


    //链式编程
    //授权
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //首页所有人可以访问, 功能页只有对应有权限的人才能访问
        //请求授权的规则
        http.authorizeRequests().antMatchers("/").permitAll()//允许所有
                .antMatchers("/level1/**").hasRole("role1")//含有level1的需要角色role1
                .antMatchers("/level2/**").hasRole("role2")//含有level2的需要角色role2
                .antMatchers("/level3/**").hasRole("role3");

        //没有权限, 默认回到登录页面(/login), 需要开启登录的页面
        http.formLogin();
        //自定义登录页面
        // http.formLogin().loginPage("/toLogin").loginProcessingUrl("/login");
        //注销, 开启了注销功能, 跳到首页
        http.logout().deleteCookies("remove").invalidateHttpSession(true).logoutSuccessUrl("/");
        //get 明文, a标签, 不安全      post 表单, 安全
        //SpringSecurity默认开启了防止csrf攻击的设置, 使用disable可以将其关闭
        http.csrf().disable();
        //开启记住我功能   cookie 默认保存两周   自定义rememberMe对应的前端的name属性
        http.rememberMe().rememberMeParameter("rememberMe");
    }

    //认证
    //密码编码:  PassWordEncoding
    //在SpringSecurity 5.0+ 新增了很多的加密方式
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //此处的数据正常情况下应该从数据库中读
        auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
                .withUser("test").password(
                        new BCryptPasswordEncoder().encode("123456")).roles("role2", "role3")
                .and()
                .withUser("root").password(
                        new BCryptPasswordEncoder().encode("123456")).roles("role1", "role2", "role3")
                .and()
                .withUser("guest").password(
                        new BCryptPasswordEncoder().encode("123456")).roles("role1");

    }
}

4.测试

访问

http://localhost:8080/level1/1

登录test用户无法访问接口,登录root即可.

5.注解

5.1 配置

Spring Security默认是禁用注解的,要想开启注解,要在继承WebSecurityConfigurerAdapter的类加@EnableMethodSecurity 注解,并在该类中将AuthenticationManager定义为Bean。

java

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)//三种注解,true代表开启
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
}

5.2 JSR-250注解

5.3 securedEnabled注解

@Secured

在方法上指定安全性要求 角色/权限,@Secured对应的角色必须要有ROLE_前缀。

5.4 prePostEnabled注解

  • @PreAuthorize --适合进入方法之前验证授权
  • @PostAuthorize --检查授权方法之后才被执行
  • @PostFilter --在方法执行之后执行,而且这里可以调用方法的返回值,然后对返回值进行过滤或处理或修改并返回
  • @PreFilter --在方法执行之前执行,而且这里可以调用方法的参数,然后对参数值进行过滤或处理或修改

5.5 异常处理

java

@Component
@Slf4j
public class AccessDeniedAuthenticationHandler implements AccessDeniedHandler {
    private final ObjectMapper objectMapper;

    public AccessDeniedAuthenticationHandler(ObjectMapper objectMapper) {
        this.objectMapper = objectMapper;
    }

    @Override
    public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException {
        log.info("没有权限");
        httpServletResponse.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
        httpServletResponse.setContentType("application/json;charset=UTF-8");
        httpServletResponse.getWriter().write(objectMapper.writeValueAsString(e.getMessage()));
    }

}
  /*      
http.authorizeRequests().anyRequest().authenticated().and().exceptionHandling().accessDeniedHandler(accessDeniedAuthenticationHandler);
*/