Spring Security基于JWT实现SSO单点登录

SSO :同一个帐号在同一个公司不同系统上登陆
Spring Security基于JWT实现SSO单点登录_第1张图片
使用SpringSecurity实现类似于SSO登陆系统是十分简单的 下面我就搭建一个DEMO
首先来看看目录的结构
Spring Security基于JWT实现SSO单点登录_第2张图片
其中sso-demo是父工程项目 sso-client 、sso-client2分别对应2个资源服务器,sso-server是认证服务器
引入的pom文件
sso-demo


<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.0modelVersion>

    <groupId>study.security.ssogroupId>
    <artifactId>sso-demoartifactId>
    <version>1.0.0-SNAPSHOTversion>
    <modules>
        <module>sso-servermodule>
        <module>sso-clientmodule>
        <module>sso-client2module>
    modules>
    <packaging>pompackaging>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>io.spring.platformgroupId>
                <artifactId>platform-bomartifactId>
                <version>Brussels-SR4version>
                <type>pomtype>
                <scope>importscope>
            dependency>
            <dependency>
                <groupId>org.springframework.cloudgroupId>
                <artifactId>spring-cloud-dependenciesartifactId>
                <version>Dalston.SR2version>
                <type>pomtype>
                <scope>importscope>
            dependency>
        dependencies>
    dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.pluginsgroupId>
                <artifactId>maven-compiler-pluginartifactId>
                <version>2.3.2version>
                <configuration>
                    <source>1.8source>
                    <target>1.8target>
                    <encoding>UTF-8encoding>
                configuration>
            plugin>
        plugins>
    build>

project>

sso-server


<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">
    <parent>
        <artifactId>sso-demoartifactId>
        <groupId>study.security.ssogroupId>
        <version>1.0.0-SNAPSHOTversion>
    parent>
    <modelVersion>4.0.0modelVersion>

    <artifactId>sso-serverartifactId>

    <dependencies>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-securityartifactId>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-webartifactId>
        dependency>
        <dependency>
            <groupId>org.springframework.security.oauthgroupId>
            <artifactId>spring-security-oauth2artifactId>
        dependency>
        <dependency>
            <groupId>org.springframework.securitygroupId>
            <artifactId>spring-security-jwtartifactId>
        dependency>

    dependencies>

project>

sso-client与sso-client2 pom 中的 是一样的

1.sso-server
现在开始搭建认证服务器

认证服务器的目录结构如下

Spring Security基于JWT实现SSO单点登录_第3张图片

/**
 * 认证服务器配置
 * Created by ZhuPengWei on 2018/1/11.
 */
@Configuration
@EnableAuthorizationServer
public class SsoAuthenticationServerConfig extends AuthorizationServerConfigurerAdapter {

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                .withClient("client1")
                .secret("client1")
                .authorizedGrantTypes("authorization_code", "refresh_token")
                .scopes("all")
                .and()
                .withClient("client2")
                .secret("client2")
                .authorizedGrantTypes("authorization_code", "refresh_token")
                .scopes("all");
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.tokenStore(jwtTokenStore()).accessTokenConverter(jwtAccessTokenConverter());
    }


    /**
     * 认证服务器的安全配置
     *
     * @param security
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        // 要访问认证服务器tokenKey的时候需要经过身份认证
        security.tokenKeyAccess("isAuthenticated()");
    }

    @Bean
    public TokenStore jwtTokenStore() {
        return new JwtTokenStore(jwtAccessTokenConverter());
    }

    @Bean
    public JwtAccessTokenConverter jwtAccessTokenConverter() {
        JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
        // 保证JWT安全的唯一方式
        jwtAccessTokenConverter.setSigningKey("ZPW");
        return jwtAccessTokenConverter;
    }
}
/**
 * 自定义用户登陆逻辑配置
 * Created by ZhuPengWei on 2018/1/13.
 */
@Configuration
public class SsoSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService userDetailsService;

    /**
     * 加密解密逻辑
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 改成表单登陆的方式 所有请求都需要认证
        http.formLogin().and().authorizeRequests().anyRequest().authenticated();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        // 用自己的登陆逻辑以及加密器
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
    }
}
/**
 * 自定义用户登陆
 * Created by ZhuPengWei on 2018/1/13.
 */
@Component
public class SsoUserDetailsService implements UserDetailsService {

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        return new User(username,
                passwordEncoder.encode("123456"),
                AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_USER"));
    }
}

其中SsoApprovalEndPoint与SsoSpelView目的是去掉登陆之后授权的效果
注解 @FrameworkEndpoint
与@RestController注解相类似
如果声明和@FrameworkEndpoint一模一样的@RequestMapping
Spring框架处理的时候会优先处理@RestController里面的

Spring Security基于JWT实现SSO单点登录_第4张图片

/**
 * 自定义认证逻辑
 * Created by ZhuPengWei on 2018/1/13.
 */
@RestController
@SessionAttributes("authorizationRequest")
public class SsoApprovalEndpoint {

    @RequestMapping("/oauth/confirm_access")
    public ModelAndView getAccessConfirmation(Map model, HttpServletRequest request) throws Exception {
        String template = createTemplate(model, request);
        if (request.getAttribute("_csrf") != null) {
            model.put("_csrf", request.getAttribute("_csrf"));
        }
        return new ModelAndView(new SsoSpelView(template), model);
    }

    protected String createTemplate(Map model, HttpServletRequest request) {
        String template = TEMPLATE;
        if (model.containsKey("scopes") || request.getAttribute("scopes") != null) {
            template = template.replace("%scopes%", createScopes(model, request)).replace("%denial%", "");
        } else {
            template = template.replace("%scopes%", "").replace("%denial%", DENIAL);
        }
        if (model.containsKey("_csrf") || request.getAttribute("_csrf") != null) {
            template = template.replace("%csrf%", CSRF);
        } else {
            template = template.replace("%csrf%", "");
        }
        return template;
    }

    private CharSequence createScopes(Map model, HttpServletRequest request) {
        StringBuilder builder = new StringBuilder("
    "); @SuppressWarnings("unchecked") Map scopes = (Map) (model.containsKey("scopes") ? model.get("scopes") : request .getAttribute("scopes")); for (String scope : scopes.keySet()) { String approved = "true".equals(scopes.get(scope)) ? " checked" : ""; String denied = !"true".equals(scopes.get(scope)) ? " checked" : ""; String value = SCOPE.replace("%scope%", scope).replace("%key%", scope).replace("%approved%", approved) .replace("%denied%", denied); builder.append(value); } builder.append("
"
); return builder.toString(); } private static String CSRF = ""; private static String DENIAL = "
%csrf%
"
; // 对源代码进行处理 隐藏授权页面,并且使他自动提交 private static String TEMPLATE = "

OAuth Approval

" + "

Do you authorize '${authorizationRequest.clientId}' to access your protected resources?

"
+ "
%csrf%%scopes%
"
+ "%denial%
"
; private static String SCOPE = "
  • %scope%: + " value='true'%approved%>Approve Deny
  • "
    ; }

    SsoSpelView 与 原来SpelView 是一样的 只不过原来SpelView 不是public的类

    application.properties

    server.port=9999
    server.context-path=/server

    2.sso-client

    相对于认证服务器 资源服务器demo的配置就十分简单了

    Spring Security基于JWT实现SSO单点登录_第5张图片

    /**
     * Created by ZhuPengWei on 2018/1/11.
     */
    @SpringBootApplication
    @RestController
    @EnableOAuth2Sso
    public class SsoClient1Application {
    
        @GetMapping("/user")
        public Authentication user(Authentication user) {
            return user;
        }
    
        public static void main(String[] args) {
            SpringApplication.run(SsoClient1Application.class, args);
        }
    }
    
    
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>SSO Client1title>
    head>
    <body>
    <h1>SSO Demo Client1h1>
    <a href="http://127.0.0.1:8060/client2/index.html">访问client2a>
    
    body>
    html>

    application.properties

    security.oauth2.client.client-id=client1
    security.oauth2.client.client-secret=client1
    #需要认证时候跳转的地址
    security.oauth2.client.user-authorization-uri=http://127.0.0.1:9999/server/oauth/authorize
    #请求令牌地址
    security.oauth2.client.access-token-uri=http://127.0.0.1:9999/server/oauth/token
    #解析
    security.oauth2.resource.jwt.key-uri=http://127.0.0.1:9999/server/oauth/token_key
    
    
    #sso
    server.port=8080
    server.context-path=/client1

    3.sso-client2

    资源服务器1和资源服务器2的目录结构是一样的,改了相关的参数

    /**
     * Created by ZhuPengWei on 2018/1/11.
     */
    @SpringBootApplication
    @RestController
    @EnableOAuth2Sso
    public class SsoClient2Application {
    
        @GetMapping("/user")
        public Authentication user(Authentication user) {
            return user;
        }
    
        public static void main(String[] args) {
            SpringApplication.run(SsoClient2Application.class, args);
        }
    }
    
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>SSO Client2title>
    head>
    <body>
    <h1>SSO Demo Client2h1>
    <a href="http://127.0.0.1:8080/client1/index.html">访问client1a>
    
    body>
    html>
    security.oauth2.client.client-id=client2
    security.oauth2.client.client-secret=client2
    #需要认证时候跳转的地址
    security.oauth2.client.user-authorization-uri=http://127.0.0.1:9999/server/oauth/authorize
    #请求令牌地址
    security.oauth2.client.access-token-uri=http://127.0.0.1:9999/server/oauth/token
    #解析
    security.oauth2.resource.jwt.key-uri=http://127.0.0.1:9999/server/oauth/token_key
    
    
    #sso
    server.port=8060
    server.context-path=/client2

    好了 基于JWT实现SSO单点登录的DEMO以及搭建完成了 下面来看看页面的效果
    在初次访问的时候
    图1

    这里写图片描述

    登陆成功之后
    图2
    Spring Security基于JWT实现SSO单点登录_第6张图片

    图3
    这里写图片描述

    注意
    写SsoApprovalEndPoint与SsoSpelView目的是去掉登陆之后授权的效果如果不写这2个类
    在初次访问的登陆成功之后是有一步授权的操作的
    比如说图1操作成功之后

    Spring Security基于JWT实现SSO单点登录_第7张图片
    点击Authorize才会跳转到图2

    你可能感兴趣的:(spring-security)