1.?前言
技術這東西吧,看別人寫的好像很簡單似的,到自己去寫的時候就各種問題,“一看就會,一做就錯”。網(wǎng)上關于實現(xiàn)SSO的文章一大堆,但是當你真的照著寫的時候就會發(fā)現(xiàn)根本不是那么回事兒,簡直讓人抓狂,尤其是對于我這樣的菜鳥。幾經曲折,終于搞定了,決定記錄下來,以便后續(xù)查看。先來看一下效果
2.?準備
2.1.?單點登錄
最常見的例子是,我們打開淘寶APP,首頁就會有天貓、聚劃算等服務的鏈接,當你點擊以后就直接跳過去了,并沒有讓你再登錄一次
下面這個圖是我再網(wǎng)上找的,我覺得畫得比較明白:
可惜有點兒不清晰,于是我又畫了個簡版的:
重要的是理解:
SSO服務端和SSO客戶端直接是通過授權以后發(fā)放Token的形式來訪問受保護的資源
相對于瀏覽器來說,業(yè)務系統(tǒng)是服務端,相對于SSO服務端來說,業(yè)務系統(tǒng)是客戶端
瀏覽器和業(yè)務系統(tǒng)之間通過會話正常訪問
不是每次瀏覽器請求都要去SSO服務端去驗證,只要瀏覽器和它所訪問的服務端的會話有效它就可以正常訪問
3.?利用OAuth2實現(xiàn)單點登錄
接下來,只講跟本例相關的一些配置,不講原理,不講為什么
眾所周知,在OAuth2在有授權服務器、資源服務器、客戶端這樣幾個角色,當我們用它來實現(xiàn)SSO的時候是不需要資源服務器這個角色的,有授權服務器和客戶端就夠了。
授權服務器當然是用來做認證的,客戶端就是各個應用系統(tǒng),我們只需要登錄成功后拿到用戶信息以及用戶所擁有的權限即可
之前我一直認為把那些需要權限控制的資源放到資源服務器里保護起來就可以實現(xiàn)權限控制,其實是我想錯了,權限控制還得通過Spring Security或者自定義攔截器來做
3.1.?Spring Security 、OAuth2、JWT、SSO
在本例中,一定要分清楚這幾個的作用
首先,SSO是一種思想,或者說是一種解決方案,是抽象的,我們要做的就是按照它的這種思想去實現(xiàn)它
其次,OAuth2是用來允許用戶授權第三方應用訪問他在另一個服務器上的資源的一種協(xié)議,它不是用來做單點登錄的,但我們可以利用它來實現(xiàn)單點登錄。在本例實現(xiàn)SSO的過程中,受保護的資源就是用戶的信息(包括,用戶的基本信息,以及用戶所具有的權限),而我們想要訪問這這一資源就需要用戶登錄并授權,OAuth2服務端負責令牌的發(fā)放等操作,這令牌的生成我們采用JWT,也就是說JWT是用來承載用戶的Access_Token的
最后,Spring Security是用于安全訪問的,這里我們我們用來做訪問權限控制
4.?認證服務器配置
4.1.?Maven依賴
<?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>
??<parent>
????<groupId>org.springframework.boot</groupId>
????<artifactId>spring-boot-starter-parent</artifactId>
????<version>2.1.3.RELEASE</version>
????<relativePath/> <!-- lookup parent from repository -->
??</parent>
??<groupId>com.cjs.sso</groupId>
??<artifactId>oauth2-sso-auth-server</artifactId>
??<version>0.0.1-SNAPSHOT</version>
??<name>oauth2-sso-auth-server</name>
??<properties>
????<java.version>1.8</java.version>
??</properties>
??<dependencies>
????<dependency>
??????<groupId>org.springframework.boot</groupId>
??????<artifactId>spring-boot-starter-data-jpa</artifactId>
????</dependency>
????<dependency>
??????<groupId>org.springframework.boot</groupId>
??????<artifactId>spring-boot-starter-data-redis</artifactId>
????</dependency>
????<dependency>
??????<groupId>org.springframework.boot</groupId>
??????<artifactId>spring-boot-starter-security</artifactId>
????</dependency>
????<dependency>
??????<groupId>org.springframework.security.oauth.boot</groupId>
??????<artifactId>spring-security-oauth2-autoconfigure</artifactId>
??????<version>2.1.3.RELEASE</version>
????</dependency>
????<dependency>
??????<groupId>org.springframework.boot</groupId>
??????<artifactId>spring-boot-starter-thymeleaf</artifactId>
????</dependency>
????<dependency>
??????<groupId>org.springframework.boot</groupId>
??????<artifactId>spring-boot-starter-web</artifactId>
????</dependency>
????<dependency>
??????<groupId>org.springframework.session</groupId>
??????<artifactId>spring-session-data-redis</artifactId>
????</dependency>
????<dependency>
??????<groupId>mysql</groupId>
??????<artifactId>mysql-connector-java</artifactId>
??????<scope>runtime</scope>
????</dependency>
????<dependency>
??????<groupId>org.projectlombok</groupId>
??????<artifactId>lombok</artifactId>
??????<optional>true</optional>
????</dependency>
????<dependency>
??????<groupId>org.springframework.boot</groupId>
??????<artifactId>spring-boot-starter-test</artifactId>
??????<scope>test</scope>
????</dependency>
????<dependency>
??????<groupId>org.springframework.security</groupId>
??????<artifactId>spring-security-test</artifactId>
??????<scope>test</scope>
????</dependency>
????<dependency>
??????<groupId>org.apache.commons</groupId>
??????<artifactId>commons-lang3</artifactId>
??????<version>3.8.1</version>
????</dependency>
????<dependency>
??????<groupId>com.alibaba</groupId>
??????<artifactId>fastjson</artifactId>
??????<version>1.2.56</version>
????</dependency>
??</dependencies>
??<build>
????<plugins>
??????<plugin>
????????<groupId>org.springframework.boot</groupId>
????????<artifactId>spring-boot-maven-plugin</artifactId>
??????</plugin>
????</plugins>
??</build>
</project>
這里面最重要的依賴是:spring-security-oauth2-autoconfigure
4.2.?application.yml
spring:
?datasource:
??url: jdbc:mysql://localhost:3306/permission
??username: root
??password: 123456
??driver-class-name: com.mysql.jdbc.Driver
?jpa:
??show-sql: true
?session:
??store-type: redis
?redis:
??host: 127.0.0.1
??password: 123456
??port: 6379
server:
?port: 8080
4.3.?AuthorizationServerConfig(重要)
package com.cjs.sso.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.security.core.token.DefaultToken;
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 javax.sql.DataSource;
/**
?* @author ChengJianSheng
?* @date 2019-02-11
?*/
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
??@Autowired
??private DataSource dataSource;
??@Override
??public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
????security.allowFormAuthenticationForClients();
????security.tokenKeyAccess("isAuthenticated()");
??}
??@Override
??public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
????clients.jdbc(dataSource);
??}
??@Override
??public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
????endpoints.accessTokenConverter(jwtAccessTokenConverter());
????endpoints.tokenStore(jwtTokenStore());
//????endpoints.tokenServices(defaultTokenServices());
??}
??/*@Primary
??@Bean
??public DefaultTokenServices defaultTokenServices() {
????DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
????defaultTokenServices.setTokenStore(jwtTokenStore());
????defaultTokenServices.setSupportRefreshToken(true);
????return defaultTokenServices;
??}*/
??@Bean
??public JwtTokenStore jwtTokenStore() {
????return new JwtTokenStore(jwtAccessTokenConverter());
??}
??@Bean
??public JwtAccessTokenConverter jwtAccessTokenConverter() {
????JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
????jwtAccessTokenConverter.setSigningKey("cjs");??//?Sets the JWT signing key
????return jwtAccessTokenConverter;
??}
}
說明:
別忘了@EnableAuthorizationServer
Token存儲采用的是JWT
客戶端以及登錄用戶這些配置存儲在數(shù)據(jù)庫,為了減少數(shù)據(jù)庫的查詢次數(shù),可以從數(shù)據(jù)庫讀出來以后再放到內存中
4.4.?WebSecurityConfig(重要)
package com.cjs.sso.config;
import com.cjs.sso.service.MyUserDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
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.builders.WebSecurity;
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;
import org.springframework.security.crypto.password.PasswordEncoder;
/**
?* @author ChengJianSheng
?* @date 2019-02-11
?*/
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
??@Autowired
??private MyUserDetailsService userDetailsService;
??@Override
??protected void configure(AuthenticationManagerBuilder auth) throws Exception {
????auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
??}
??@Override
??public void configure(WebSecurity web) throws Exception {
????web.ignoring().antMatchers("/assets/**", "/css/**", "/images/**");
??}
??@Override
??protected void configure(HttpSecurity http) throws Exception {
????http.formLogin()
????????.loginPage("/login")
????????.and()
????????.authorizeRequests()
????????.antMatchers("/login").permitAll()
????????.anyRequest()
????????.authenticated()
????????.and().csrf().disable().cors();
??}
??@Bean
??public PasswordEncoder passwordEncoder() {
????return new BCryptPasswordEncoder();
??}
}
4.5.?自定義登錄頁面(一般來講都是要自定義的)
package com.cjs.sso.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
/**
* @author ChengJianSheng
* @date 2019-02-12
*/
@Controller
public class LoginController {
? ? @GetMapping("/login")
? ? public String login() {
? ? ? ? return "login";
? ? }
? ? @GetMapping("/")
? ? public String index() {
? ? ? ? return "index";
? ? }
}
自定義登錄頁面的時候,只需要準備一個登錄頁面,然后寫個Controller令其可以訪問到即可,登錄頁面表單提交的時候method一定要是post,最重要的時候action要跟訪問登錄頁面的url一樣
千萬記住了,訪問登錄頁面的時候是GET請求,表單提交的時候是POST請求,其它的就不用管了
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
? ? <meta charset="utf-8">
? ? <meta http-equiv="X-UA-Compatible" content="IE=edge">
? ? <title>Ela Admin - HTML5 Admin Template</title>
? ? <meta name="description" content="Ela Admin - HTML5 Admin Template">
? ? <meta name="viewport" content="width=device-width, initial-scale=1">
? ? <link type="text/css" rel="stylesheet" th:href="@{/assets/css/normalize.css}">
? ? <link type="text/css" rel="stylesheet" th:href="@{/assets/bootstrap-4.3.1-dist/css/bootstrap.min.css}">
? ? <link type="text/css" rel="stylesheet" th:href="@{/assets/css/font-awesome.min.css}">
? ? <link type="text/css" rel="stylesheet" th:href="@{/assets/css/style.css}">
</head>
<body class="bg-dark">
<div class="sufee-login d-flex align-content-center flex-wrap">
? ? <div class="container">
? ? ? ? <div class="login-content">
? ? ? ? ? ? <div class="login-logo">
? ? ? ? ? ? ? ? <h1 style="color: #57bf95;">歡迎來到王者榮耀</h1>
? ? ? ? ? ? </div>
? ? ? ? ? ? <div class="login-form">
? ? ? ? ? ? ? ? <form th:action="@{/login}" method="post">
? ? ? ? ? ? ? ? ? ? <div class="form-group">
? ? ? ? ? ? ? ? ? ? ? ? <label>Username</label>
? ? ? ? ? ? ? ? ? ? ? ? <input type="text" class="form-control" name="username" placeholder="Username">
? ? ? ? ? ? ? ? ? ? </div>
? ? ? ? ? ? ? ? ? ? <div class="form-group">
? ? ? ? ? ? ? ? ? ? ? ? <label>Password</label>
? ? ? ? ? ? ? ? ? ? ? ? <input type="password" class="form-control" name="password" placeholder="Password">
? ? ? ? ? ? ? ? ? ? </div>
? ? ? ? ? ? ? ? ? ? <div class="checkbox">
? ? ? ? ? ? ? ? ? ? ? ? <label>
? ? ? ? ? ? ? ? ? ? ? ? ? ? <input type="checkbox"> Remember Me
? ? ? ? ? ? ? ? ? ? ? ? </label>
? ? ? ? ? ? ? ? ? ? ? ? <label class="pull-right">
? ? ? ? ? ? ? ? ? ? ? ? ? ? <a href="#">Forgotten Password?</a>
? ? ? ? ? ? ? ? ? ? ? ? </label>
? ? ? ? ? ? ? ? ? ? </div>
? ? ? ? ? ? ? ? ? ? <button type="submit" class="btn btn-success btn-flat m-b-30 m-t-30" style="font-size: 18px;">登錄</button>
? ? ? ? ? ? ? ? </form>
? ? ? ? ? ? </div>
? ? ? ? </div>
? ? </div>
</div>
<script type="text/javascript" th:src="@{/assets/js/jquery-2.1.4.min.js}"></script>
<script type="text/javascript" th:src="@{/assets/bootstrap-4.3.1-dist/js/bootstrap.min.js}"></script>
<script type="text/javascript" th:src="@{/assets/js/main.js}"></script>
</body>
</html>
4.6.?定義客戶端
4.7.?加載用戶
登錄賬戶
package com.cjs.sso.domain;
import lombok.Data;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;
import java.util.Collection;
/**
* 大部分時候直接用User即可不必擴展
* @author ChengJianSheng
* @date 2019-02-11
*/
@Data
public class MyUser extends User {
? ? private Integer departmentId;? //? 舉個例子,部門ID
? ? private String mobile;? //? 舉個例子,假設我們想增加一個字段,這里我們增加一個mobile表示手機號
? ? public MyUser(String username, String password, Collection<? extends GrantedAuthority> authorities) {
? ? ? ? super(username, password, authorities);
? ? }
? ? public MyUser(String username, String password, boolean enabled, boolean accountNonExpired, boolean credentialsNonExpired, boolean accountNonLocked, Collection<? extends GrantedAuthority> authorities) {
? ? ? ? super(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities);
? ? }
}
加載登錄賬戶
package com.cjs.sso.service;
import com.alibaba.fastjson.JSON;
import com.cjs.sso.domain.MyUser;
import com.cjs.sso.entity.SysPermission;
import com.cjs.sso.entity.SysUser;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
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.Service;
import org.springframework.util.CollectionUtils;
import java.util.ArrayList;
import java.util.List;
/**
* @author ChengJianSheng
* @date 2019-02-11
*/
@Slf4j
@Service
public class MyUserDetailsService implements UserDetailsService {
? ? @Autowired
? ? private PasswordEncoder passwordEncoder;
? ? @Autowired
? ? private UserService userService;
? ? @Autowired
? ? private PermissionService permissionService;
? ? @Override
? ? public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
? ? ? ? SysUser sysUser = userService.getByUsername(username);
? ? ? ? if (null == sysUser) {
? ? ? ? ? ? log.warn("用戶{}不存在", username);
? ? ? ? ? ? throw new UsernameNotFoundException(username);
? ? ? ? }
? ? ? ? List<SysPermission> permissionList = permissionService.findByUserId(sysUser.getId());
? ? ? ? List<SimpleGrantedAuthority> authorityList = new ArrayList<>();
? ? ? ? if (!CollectionUtils.isEmpty(permissionList)) {
? ? ? ? ? ? for (SysPermission sysPermission : permissionList) {
? ? ? ? ? ? ? ? authorityList.add(new SimpleGrantedAuthority(sysPermission.getCode()));
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? MyUser myUser = new MyUser(sysUser.getUsername(), passwordEncoder.encode(sysUser.getPassword()), authorityList);
? ? ? ? log.info("登錄成功!用戶: {}", JSON.toJSONString(myUser));
? ? ? ? return myUser;
? ? }
}
4.8.?驗證
當我們看到這個界面的時候,表示認證服務器配置完成
5.?兩個客戶端
5.1.?Maven依賴
<?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>
? ? <parent>
? ? ? ? <groupId>org.springframework.boot</groupId>
? ? ? ? <artifactId>spring-boot-starter-parent</artifactId>
? ? ? ? <version>2.1.3.RELEASE</version>
? ? ? ? <relativePath/> <!-- lookup parent from repository -->
? ? </parent>
? ? <groupId>com.cjs.sso</groupId>
? ? <artifactId>oauth2-sso-client-member</artifactId>
? ? <version>0.0.1-SNAPSHOT</version>
? ? <name>oauth2-sso-client-member</name>
? ? <description>Demo project for Spring Boot</description>
? ? <properties>
? ? ? ? <java.version>1.8</java.version>
? ? </properties>
? ? <dependencies>
? ? ? ? <dependency>
? ? ? ? ? ? <groupId>org.springframework.boot</groupId>
? ? ? ? ? ? <artifactId>spring-boot-starter-data-jpa</artifactId>
? ? ? ? </dependency>
? ? ? ? <dependency>
? ? ? ? ? ? <groupId>org.springframework.boot</groupId>
? ? ? ? ? ? <artifactId>spring-boot-starter-oauth2-client</artifactId>
? ? ? ? </dependency>
? ? ? ? <dependency>
? ? ? ? ? ? <groupId>org.springframework.boot</groupId>
? ? ? ? ? ? <artifactId>spring-boot-starter-security</artifactId>
? ? ? ? </dependency>
? ? ? ? <dependency>
? ? ? ? ? ? <groupId>org.springframework.security.oauth.boot</groupId>
? ? ? ? ? ? <artifactId>spring-security-oauth2-autoconfigure</artifactId>
? ? ? ? ? ? <version>2.1.3.RELEASE</version>
? ? ? ? </dependency>
? ? ? ? <dependency>
? ? ? ? ? ? <groupId>org.springframework.boot</groupId>
? ? ? ? ? ? <artifactId>spring-boot-starter-thymeleaf</artifactId>
? ? ? ? </dependency>
? ? ? ? <dependency>
? ? ? ? ? ? <groupId>org.thymeleaf.extras</groupId>
? ? ? ? ? ? <artifactId>thymeleaf-extras-springsecurity5</artifactId>
? ? ? ? ? ? <version>3.0.4.RELEASE</version>
? ? ? ? </dependency>
? ? ? ? <dependency>
? ? ? ? ? ? <groupId>org.springframework.boot</groupId>
? ? ? ? ? ? <artifactId>spring-boot-starter-web</artifactId>
? ? ? ? </dependency>
? ? ? ? <dependency>
? ? ? ? ? ? <groupId>com.h2database</groupId>
? ? ? ? ? ? <artifactId>h2</artifactId>
? ? ? ? ? ? <scope>runtime</scope>
? ? ? ? </dependency>
? ? ? ? <dependency>
? ? ? ? ? ? <groupId>org.projectlombok</groupId>
? ? ? ? ? ? <artifactId>lombok</artifactId>
? ? ? ? ? ? <optional>true</optional>
? ? ? ? </dependency>
? ? ? ? <dependency>
? ? ? ? ? ? <groupId>org.springframework.boot</groupId>
? ? ? ? ? ? <artifactId>spring-boot-starter-test</artifactId>
? ? ? ? ? ? <scope>test</scope>
? ? ? ? </dependency>
? ? ? ? <dependency>
? ? ? ? ? ? <groupId>org.springframework.security</groupId>
? ? ? ? ? ? <artifactId>spring-security-test</artifactId>
? ? ? ? ? ? <scope>test</scope>
? ? ? ? </dependency>
? ? </dependencies>
? ? <build>
? ? ? ? <plugins>
? ? ? ? ? ? <plugin>
? ? ? ? ? ? ? ? <groupId>org.springframework.boot</groupId>
? ? ? ? ? ? ? ? <artifactId>spring-boot-maven-plugin</artifactId>
? ? ? ? ? ? </plugin>
? ? ? ? </plugins>
? ? </build>
</project>
5.2.?application.yml
server:
? port: 8082
? servlet:
? ? context-path: /memberSystem
security:
? oauth2:
? ? client:
? ? ? client-id: UserManagement
? ? ? client-secret: user123
? ? ? access-token-uri: http://localhost:8080/oauth/token
? ? ? user-authorization-uri: http://localhost:8080/oauth/authorize
? ? resource:
? ? ? jwt:
? ? ? ? key-uri: http://localhost:8080/oauth/token_key
這里context-path不要設成/,不然重定向獲取code的時候回被攔截
5.3.?WebSecurityConfig
package com.cjs.example.config;
import com.cjs.example.util.EnvironmentUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.security.oauth2.client.EnableOAuth2Sso;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
/**
* @author ChengJianSheng
* @date 2019-03-03
*/
@EnableOAuth2Sso
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
? ? @Autowired
? ? private EnvironmentUtils environmentUtils;
? ? @Override
? ? public void configure(WebSecurity web) throws Exception {
? ? ? ? web.ignoring().antMatchers("/bootstrap/**");
? ? }
? ? @Override
? ? protected void configure(HttpSecurity http) throws Exception {
? ? ? ? if ("local".equals(environmentUtils.getActiveProfile())) {
? ? ? ? ? ? http.authorizeRequests().anyRequest().permitAll();
? ? ? ? }else {
? ? ? ? ? ? http.logout().logoutSuccessUrl("http://localhost:8080/logout")
? ? ? ? ? ? ? ? ? ? .and()
? ? ? ? ? ? ? ? ? ? .authorizeRequests()
? ? ? ? ? ? ? ? ? ? .anyRequest().authenticated()
? ? ? ? ? ? ? ? ? ? .and()
? ? ? ? ? ? ? ? ? ? .csrf().disable();
? ? ? ? }
? ? }
}
?說明:
最重要的注解是@EnableOAuth2Sso
權限控制這里采用Spring Security方法級別的訪問控制,結合Thymeleaf可以很容易做權限控制
順便多提一句,如果是前后端分離的話,前端需求加載用戶的權限,然后判斷應該顯示那些按鈕那些菜單
5.4.?MemberController
package com.cjs.example.controller;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.security.Principal;
/**
* @author ChengJianSheng
* @date 2019-03-03
*/
@Controller
@RequestMapping("/member")
public class MemberController {
? ? @GetMapping("/list")
? ? public String list() {
? ? ? ? return "member/list";
? ? }
? ? @GetMapping("/info")
? ? @ResponseBody
? ? public Principal info(Principal principal) {
? ? ? ? return principal;
? ? }
? ? @GetMapping("/me")
? ? @ResponseBody
? ? public Authentication me(Authentication authentication) {
? ? ? ? return authentication;
? ? }
? ? @PreAuthorize("hasAuthority('member:save')")
? ? @ResponseBody
? ? @PostMapping("/add")
? ? public String add() {
? ? ? ? return "add";
? ? }
? ? @PreAuthorize("hasAuthority('member:detail')")
? ? @ResponseBody
? ? @GetMapping("/detail")
? ? public String detail() {
? ? ? ? return "detail";
? ? }
}
5.5. Order項目跟它是一樣的
server:
? port: 8083
? servlet:
? ? context-path: /orderSystem
security:
? oauth2:
? ? client:
? ? ? client-id: OrderManagement
? ? ? client-secret: order123
? ? ? access-token-uri: http://localhost:8080/oauth/token
? ? ? user-authorization-uri: http://localhost:8080/oauth/authorize
? ? resource:
? ? ? jwt:
? ? ? ? key-uri: http://localhost:8080/oauth/token_key
5.6.?關于退出
退出就是清空用于與SSO客戶端建立的所有的會話,簡單的來說就是使所有端點的Session失效,如果想做得更好的話可以令Token失效,但是由于我們用的JWT,故而撤銷Token就不是那么容易,關于這一點,在官網(wǎng)上也有提到:
本例中采用的方式是在退出的時候先退出業(yè)務服務器,成功以后再回調認證服務器,但是這樣有一個問題,就是需要主動依次調用各個業(yè)務服務器的logout
6.?工程結構
歡迎工作一到五年的Java工程師朋友們加入Java程序員開發(fā): 721575865
群內提供免費的Java架構學習資料(里面有高可用、高并發(fā)、高性能及分布式、Jvm性能調優(yōu)、Spring源碼,MyBatis,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx等多個知識點的架構資料)合理利用自己每一分每一秒的時間來學習提升自己,不要再用"沒有時間“來掩飾自己思想上的懶惰!趁年輕,使勁拼,給未來的自己一個交代!