SpringBoot实例:医院统一信息平台(单点登录测试)

前面已经完成了Oauth2服务器的开发,简单测试了下access_token的获取。spring security oauth还支持SSO单点登录服务端与客户端。我们现在做的是用户系统,调整一下代码,做SSO服务端。

授权页面

增加一个授权页面,在用户在别的系统中登录的时候询问用户是否允许系统向用户系统取某些信息。

<!DOCTYPE html>
<html lang="zh-CN" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8"></meta>
    <title>授权</title>
</head>
<body>
    <p><span th:text="${client}"></span> 程序想要请您授权以下内容:</p>
    <form id="confirmationForm" name="confirmationForm" th:action="@{/oauth/authorize}" method="post">
        <input name="user_oauth_approval" value="true" type="hidden" />
        <div th:each="scope: ${scopes}">
            <span th:text="${scope.key}">Some Scope</span>
            <span>
                <label>
                    <input type="radio" th:name="${scope.key}" value="true" th:checked="${scope.value}"/> 许可
                </label>
                <label>
                    <input type="radio" th:name="${scope.key}" value="false" th:checked="${!scope.value}"/> 拒绝
                </label>
            </span>
        </div>
        <button class="btn btn-primary" type="submit">确认并授权</button>
    </form>
    <p>请许可上述授权范围并点击确认并授权按钮继续。</p>
</body>
</html>

授权控制器

在com.biboheart.huip.user.security.controller创建SecurityController.java文件。主要实现用户信息获取,用户权限获取等接口。授权页面的控制器也放在这里

package com.biboheart.huip.user.security.controller;

import java.security.Principal;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.common.util.OAuth2Utils;
import org.springframework.security.oauth2.provider.AuthorizationRequest;
import org.springframework.security.oauth2.provider.approval.Approval;
import org.springframework.security.oauth2.provider.approval.Approval.ApprovalStatus;
import org.springframework.security.oauth2.provider.approval.ApprovalStore;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.servlet.ModelAndView;

import com.biboheart.brick.model.BhResponseResult;

@Controller
@SessionAttributes("authorizationRequest")
public class SecurityController {
    @Autowired
    private ApprovalStore approvalStore;
    
    @RequestMapping(value = "/user/info")
    @ResponseBody
    public ResponseEntity<?> user(Principal principal) {
        return new ResponseEntity<>(principal, HttpStatus.OK);
    }
    
    @RequestMapping(value = "/user/name")
    @ResponseBody
    public String username(Principal principal) {
        return principal.getName();
    }
    
    @RequestMapping(value = "/user/authorities")
    @ResponseBody
    public BhResponseResult<?> authorities(Principal principal) {
        Collection<? extends GrantedAuthority> authorities = SecurityContextHolder.getContext().getAuthentication().getAuthorities();
        return new BhResponseResult<>(0, "操作成功", authorities);
    }
    
    @RequestMapping("/oauth/confirm_access")
    public ModelAndView getAccessConfirmation(Map<String, Object> model, Principal principal) {
        AuthorizationRequest clientAuth = (AuthorizationRequest) model.remove("authorizationRequest");
        model.put("auth_request", clientAuth);
        model.put("client", "client");
        Map<String, String> scopes = new LinkedHashMap<String, String>();
        for (String scope : clientAuth.getScope()) {
            scopes.put(OAuth2Utils.SCOPE_PREFIX + scope, "false");
        }
        for (Approval approval : approvalStore.getApprovals(principal.getName(), "client")) {
            if (clientAuth.getScope().contains(approval.getScope())) {
                scopes.put(OAuth2Utils.SCOPE_PREFIX + approval.getScope(),
                        approval.getStatus() == ApprovalStatus.APPROVED ? "true" : "false");
            }
        }
        model.put("scopes", scopes);
        System.out.println(model);
        return new ModelAndView("access_confirmation", model);
    }
}

调整配置

SecurityConfiguration.java中增加放行的point


    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // @formatter:off
        http
            .csrf().disable()
            .authorizeRequests()
                .antMatchers("/", "/home", "/oauth/token", "/oauth/authorize").permitAll()
                .anyRequest().authenticated()
                .and()
            .formLogin()
                .loginPage("/login")
                .permitAll()
                .and()
            .logout()
                .permitAll();
        http.addFilterBefore(mobileCodeAuthenticationProcessingFilter(), UsernamePasswordAuthenticationFilter.class);
        // @formatter:on
    }

修改AuthorizationServerConfiguration

package com.biboheart.huip.user.security;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
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.approval.ApprovalStore;
import org.springframework.security.oauth2.provider.approval.TokenApprovalStore;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore;

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
    @Autowired
    @Qualifier("authenticationManagerBean")
    private AuthenticationManager authenticationManager;

    @Autowired
    private TokenStore tokenStore;

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
            .withClient("client")
            .secret(new BCryptPasswordEncoder().encode("secret"))
            .authorizedGrantTypes("client_credentials", "password", "refresh_token", "authorization_code")
            .scopes("all", "user_info")
            .autoApprove(false) // true: 不会跳转到授权页面
            .redirectUris("http://localhost:8080/login");
    }
    
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints
            .authenticationManager(this.authenticationManager)
            .tokenStore(tokenStore);
    }
    
    @Override
    public void configure(AuthorizationServerSecurityConfigurer oauthServer)
            throws Exception {
        oauthServer
            .tokenKeyAccess("permitAll()")
            .checkTokenAccess("isAuthenticated()");
    }
    
    @Bean
    public TokenStore tokenStore() {
        return new InMemoryTokenStore();
    }
    
    @Bean
    public ApprovalStore approvalStore() {
        TokenApprovalStore store = new TokenApprovalStore();
        store.setTokenStore(tokenStore);
        return store;
    }
    
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

}

如果autoApprove设置为true的话,客户端就不会进入用户授权页面。对于本系统的客户端就不需要用户确认授权操作,这时候设置为true就行。现在我们是要测试授权页面的。所以设置为false

创建客户端

创建项目dssoclient,因为这个项目源码是不上传的,所以把项目中每个文件的代码都会贴入。
目录结构:


目录结构

pom.xml

<?xml version="1.0"?>
<project
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
    xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <modelVersion>4.0.0</modelVersion>
    
    <parent>
        <groupId>com.biboheart.demos</groupId>
        <artifactId>bootparent</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    
    <artifactId>dssoclient</artifactId>
    <name>dssoclient</name>
    <url>http://maven.apache.org</url>
    
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        
        <dependency>
            <groupId>org.springframework.security.oauth</groupId>
            <artifactId>spring-security-oauth2</artifactId>
            <version>2.3.3.RELEASE</version>
        </dependency>
        
        <dependency>
            <groupId>org.springframework.security.oauth.boot</groupId>
            <artifactId>spring-security-oauth2-autoconfigure</artifactId>
            <version>2.0.0.RELEASE</version>
        </dependency>
        
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
    </dependencies>
</project>

DssoclientApplication

package com.biboheart.demo.dssoclient;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DssoclientApplication {
    public static void main(String[] args) {
        SpringApplication.run(DssoclientApplication.class, args);
    }
}

SecurityConfiguration

package com.biboheart.demo.dssoclient.security;

import java.util.Arrays;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.security.oauth2.client.EnableOAuth2Sso;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.oauth2.client.OAuth2ClientContext;
import org.springframework.security.oauth2.client.OAuth2RestTemplate;
import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails;
import org.springframework.security.oauth2.client.token.AccessTokenProviderChain;
import org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeAccessTokenProvider;

@Configuration
@EnableOAuth2Sso
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
    @Autowired
    private OAuth2ClientContext oauth2ClientContext;
    
    @Bean
    public OAuth2RestTemplate oauth2RestTemplate(OAuth2ProtectedResourceDetails resource) {
        OAuth2RestTemplate template = new OAuth2RestTemplate(resource, oauth2ClientContext);

        AuthorizationCodeAccessTokenProvider authCodeProvider = new AuthorizationCodeAccessTokenProvider();
        authCodeProvider.setStateMandatory(false);
        AccessTokenProviderChain provider = new AccessTokenProviderChain(Arrays.asList(authCodeProvider));
        template.setAccessTokenProvider(provider);
        return template;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // @formatter:off
        http
            .csrf().disable()
            .authorizeRequests()
                .antMatchers("/", "/home").permitAll()
                .anyRequest().authenticated()
                .and()
            .formLogin()
                .loginPage("/login")
                .permitAll()
                .and()
            .logout().permitAll()
                .and()
            .httpBasic().disable();
        // @formatter:on
    }
    
}

HelloController

package com.biboheart.demo.dssoclient.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class HelloController {
    
    @RequestMapping(value = { "/", "/home" }, method = RequestMethod.GET)
    public String homePage() {
        return "index";
    }

    @RequestMapping(value = "/hello", method = RequestMethod.GET)
    public String hello() {
        return "hello";
    }
    
}

application.yml

server:
  port: 8080
spring:
  output:
    ansi:
      enabled: DETECT
  http:
    encoding:
      charset: UTF-8
    multipart:
      maxFileSize: -1
      maxRequestSize: 100MB
security:
  oauth2:
    client:
      clientId: client
      clientSecret: secret
      accessTokenUri: http://192.168.2.105:8180/oauth/token
      userAuthorizationUri: http://192.168.2.105:8180/oauth/authorize
      #tokenName: access_token
      #authenticationScheme: query
      #clientAuthenticationScheme: form
      #以下两行为自定义回调地址
      #pre-established-redirect-uri: http://localhost:8080/hello
      #use-current-uri: false
    resource:
      userInfoUri: http://192.168.2.105:8180/user/info
      #tokenInfoUri: http://192.168.2.105:8180/oauth/check_token
      #prefer-token-info: false
    basic:
      enabled: false
# LOGGING
logging:
  level:
    root: info

index.html

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8"></meta>
    <title>Spring Security入门</title>
</head>
<body>
    <h1>欢迎使用Spring Security!</h1>
    <p>
        点击 <a href="/hello">这里</a> 打个招呼吧
    </p>
</body>
</html>

hello.html

<!DOCTYPE html>
<html lang="zh-CN" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8"></meta>
    <title>Hello World!</title>
</head>
<body>
    <h1>Hello world!</h1>
</body>
</html>

login.html没有使用

测试项目

运行用户系统(服务端项目)
运行dssoclient项目(客户端项目)
访问http://localhost:8080 客户端项目的首页,首页不受控制

首页

点击“这里”,跳转服务端的登录页面
登录页

选择一种登录方式后,进入授权页面
授权

许可后点击确认并授权,完成后进入hello页面
hello

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容