认证中心CAS实现SSO单点登录

CAS(Central Authentication Service)是一种单点登录(SSO)协议和实现方式。它允许用户只需进行一次身份验证,然后就能访问多个不同的应用程序或服务,而无需在每个应用程序中单独进行身份验证。

项目结构

父项目

引入子模块

<modules>
		<module>clinet1</module>
		<module>clinet2</module>
		<module>service</module>
</modules>

引入依赖

<!--方便地管理和控制Spring Boot及其相关依赖项的版本。-->
			<dependency>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-dependencies</artifactId>
				<version>2.0.8.RELEASE</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
			<!--管理Spring Boot和其他相关库的版本-->
			<dependency>
				<groupId>io.spring.platform</groupId>
				<artifactId>platform-bom</artifactId>
				<version>Cairo-RELEASE</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
			<!--管理Spring Cloud各组件的版本-->
			<dependency>
				<groupId>org.springframework.cloud</groupId>
				<artifactId>spring-cloud-dependencies</artifactId>
				<version>Finchley.SR2</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>

完整POM文件

<?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>com.example</groupId>
	<artifactId>springtwo</artifactId>
	<version>0.0.1-SNAPSHOT</version>

	<name>springtwo</name>
	<description>springtwo</description>
	<modules>
		<module>clinet1</module>
		<module>clinet2</module>
		<module>service</module>
	</modules>

	<dependencyManagement>

		<dependencies>
			<!--方便地管理和控制Spring Boot及其相关依赖项的版本。-->
			<dependency>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-dependencies</artifactId>
				<version>2.0.8.RELEASE</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
			<!--管理Spring Boot和其他相关库的版本-->
			<dependency>
				<groupId>io.spring.platform</groupId>
				<artifactId>platform-bom</artifactId>
				<version>Cairo-RELEASE</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
			<!--管理Spring Cloud各组件的版本-->
			<dependency>
				<groupId>org.springframework.cloud</groupId>
				<artifactId>spring-cloud-dependencies</artifactId>
				<version>Finchley.SR2</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>

		</dependencies>

	</dependencyManagement>

</project>

CAS服务项目

授权认证中心本质就是一个 Spring Boot应用

引入依赖

<dependency>
   <groupId>org.springframework.cloud</groupId>
   <artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>

配置文件

server.port=8085
server.servlet.context-path=/uac

securityconfig

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
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.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    @Bean
    public AuthenticationManager authenticationManager() throws Exception {
        return super.authenticationManager();
    }

    @Autowired
    private UserDetailsService userDetailsService;

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public DaoAuthenticationProvider authenticationProvider() {
        DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
        authenticationProvider.setUserDetailsService(userDetailsService);
        authenticationProvider.setPasswordEncoder(passwordEncoder());
        authenticationProvider.setHideUserNotFoundExceptions(false);
        return authenticationProvider;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 接口配置
        http
                .requestMatchers().antMatchers("/oauth/**","/login/**","/logout/**")
                .and()
                .authorizeRequests()
                .antMatchers("/oauth/**").authenticated()
                .and()
                .formLogin().permitAll();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(authenticationProvider());
    }

}

UserDetailsService

用户登录验证层面

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;

@Component
public class SheepUserDetailsService implements UserDetailsService {

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
        // 登录用户和密码(写死的),可以从数据库去拿
        if( !"myname".equals(s) )
            throw new UsernameNotFoundException("用户" + s + "不存在" );

        return new User( s, passwordEncoder.encode("123456"), AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_NORMAL,ROLE_MEDIUM"));
    }
}

oauth2Config

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;

import java.util.concurrent.TimeUnit;


@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {

        // 方法定义了两个内存中的客户端应用通行证
        clients.inMemory()
                .withClient("clinet1")
                // 表示客户端应用通行证的密码
                .secret(new BCryptPasswordEncoder().encode("Passkey"))
                // 表示客户端应用通行证可以使用的授权类型:授权码模式和刷新令牌
                .authorizedGrantTypes("authorization_code", "refresh_token")
                // 可以访问所有资源
                .scopes("all")
                // 需要用户手动批准授权请求
                .autoApprove(false)
                .and()
                .withClient("clinet2")
                .secret(new BCryptPasswordEncoder().encode("Passkey"))
                .authorizedGrantTypes("authorization_code", "refresh_token")
                .scopes("all")
                .autoApprove(false);
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        // 设置使用 JWT 令牌存储和转换器。
        endpoints.tokenStore(jwtTokenStore()).accessTokenConverter(jwtAccessTokenConverter());
        // 获取默认的令牌服务。
        DefaultTokenServices tokenServices = (DefaultTokenServices) endpoints.getDefaultAuthorizationServerTokenServices();
        // 设置令牌服务的令牌存储。
        tokenServices.setTokenStore(endpoints.getTokenStore());
        // 启用令牌服务支持刷新令牌
        tokenServices.setSupportRefreshToken(true);
        // 设置令牌服务的客户端详情服务。
        tokenServices.setClientDetailsService(endpoints.getClientDetailsService());
        // 设置令牌服务的令牌增强器。
        tokenServices.setTokenEnhancer(endpoints.getTokenEnhancer());
        // 设置访问令牌的有效期为一天。
        tokenServices.setAccessTokenValiditySeconds((int) TimeUnit.DAYS.toSeconds(1));
        // 将配置好的令牌服务设置到授权服务器的端点上。
        endpoints.tokenServices(tokenServices);
    }

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        // 只有通过身份认证的用户才能访问令牌密钥端点。
        security.tokenKeyAccess("isAuthenticated()");
    }

    @Bean
    public TokenStore jwtTokenStore() {
        //  Spring Security OAuth2 提供的一个具体实现,用于将令牌存储为 JWT 格式,并提供相应的方法来操作和检索令牌。
        //  使用 JWT Token 转换器 jwtAccessTokenConverter() 来存储和管理令牌
        return new JwtTokenStore(jwtAccessTokenConverter());
    }

    @Bean
    public JwtAccessTokenConverter jwtAccessTokenConverter(){
        // 使用 JWT Token 转换器来处理令牌的转换、签名和验证操作,并将 JWT Token 中的内容与签名密钥"testKey"进行关联。
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setSigningKey("MyKey");
        return converter;
    }

}

Clinet1项目

客户端应用,clinet2项目与下面同理。

引入依赖

<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-oauth2</artifactId>
  <!--<version>2.0.1.RELEASE</version>-->
</dependency>

配置文件

auth-server=http://localhost:8085/uac
server.port=8086
security.oauth2.client.client-id=clinet1
security.oauth2.client.client-secret=Passkey
security.oauth2.client.user-authorization-uri=${auth-server}/oauth/authorize
security.oauth2.client.access-token-uri=${auth-server}/oauth/token
security.oauth2.resource.jwt.key-uri=${auth-server}/oauth/token_key

WebsecurityConfig

import org.springframework.boot.autoconfigure.security.oauth2.client.EnableOAuth2Sso;
import org.springframework.context.annotation.Configuration;
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;

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
@EnableOAuth2Sso
public class ClientWebsecurityConfigurer extends WebSecurityConfigurerAdapter {

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.antMatcher("/**").authorizeRequests()
                .anyRequest().authenticated();
    }
}

controller

应用接口

import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TestController {

    @GetMapping("/normal")
    @PreAuthorize("hasAuthority('ROLE_NORMAL')")
    public String normal( ) {
        return "normal permission test success !!!";
    }

    @GetMapping("/medium")
    @PreAuthorize("hasAuthority('ROLE_MEDIUM')")
    public String medium() {
        return "medium permission test success !!!";
    }

    @GetMapping("/admin")
    @PreAuthorize("hasAuthority('ROLE_ADMIN')")
    public String admin() {
        return "admin permission test success !!!";
    }
}

测试

启动服务 

 访问接口,跳转到登录页面

 输入用户密码,跳转到授权页面,确认授权

 跳转成功

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值