準(zhǔn)備工作
準(zhǔn)備SQL
CREATE TABLE `user` (
`id` int NOT NULL AUTO_INCREMENT,
`username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL,
`password` varchar(255) DEFAULT NULL,
`salt` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=16 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
添加用戶
INSERT INTO
`security_oauth_demo`.`user`(`id``username`,`password`, `salt`)
VALUES
(1, 'admin''$2a$10$WuZUcd3Uc1IQ8uNTuIPrvuoToxiZ/CNtfgQL/M/vFQu63pYYQExZK', '1');
添加依賴
<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>
<properties>
<java.version>1.8</java.version>
<druid.version>1.1.14</druid.version>
<fastjson.version>1.2.68</fastjson.version>
<security.oauth2.version>2.3.5.RELEASE</security.oauth2.version>
</properties>
<dependencies>
<!-- SpringBoot 核心包 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- SpringBoot 測(cè)試 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- SpringBoot Web容器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- spring-boot-devtools -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional> <!-- 表示依賴不會(huì)傳遞 -->
</dependency>
<!-- spring security 安全認(rèn)證 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!--若依使用的 spring security oauth2 開放授權(quán) -->
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<version>${security.oauth2.version}</version>
</dependency>
<!-- Mysql驅(qū)動(dòng)包 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- SpringBoot集成mybatis框架 -->
<!--mybatis-plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.3.1</version>
</dependency>
<!--阿里數(shù)據(jù)庫連接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>${druid.version}</version>
</dependency>
<!--常用工具類 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<!-- 阿里JSON解析器 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
<!--Spring框架基本的核心工具-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
</dependency>
<!-- jwt-->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-jwt</artifactId>
<version>1.1.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.26</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
準(zhǔn)備UserInfo
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serializable;
@Data
@TableName("user")
public class UserInfo implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "id",type = IdType.AUTO)
private Integer id;
private String username;
private String password;
private String salt;
}
UserMapper
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.yc.entity.UserInfo;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface UserMapper extends BaseMapper<UserInfo> {
}
UserService
import com.baomidou.mybatisplus.extension.service.IService;
import com.yc.entity.UserInfo;
public interface UserService extends IService<UserInfo> {
String login(UserInfo user);
String add(UserInfo user);
String findOne(UserInfo user);
String update(UserInfo user);
}
UserServiceImpl
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yc.entity.UserInfo;
import com.yc.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, UserInfo> implements UserService {
@Autowired
UserMapper userMapper;
/**
* 模擬登錄
* @param user
* @return
*/
@Override
public String login(UserInfo user) {
UserInfo sysUser = userMapper.selectOne(
new QueryWrapper<UserInfo>().lambda().eq(UserInfo::getUsername, user.getUsername()));
JSONObject jsonObject = new JSONObject();
jsonObject.put("code", "200");
jsonObject.put("msg", "登錄成功");
return jsonObject.toString();
}
@Override
public String add(UserInfo user) {
int insert = userMapper.insert(user);
return insert +"";
}
@Override
public String findOne(UserInfo user) {
UserInfo sysUser = userMapper.selectOne(
new QueryWrapper<UserInfo>().lambda().eq(UserInfo::getUsername, user.getUsername()));
return sysUser.getUsername();
}
@Override
public String update(UserInfo user) {
QueryWrapper<UserInfo> wrapper = new QueryWrapper<>();
wrapper.eq("id", user.getId());
int update = userMapper.update(user, wrapper);
return "成功更新"+ update+ "條數(shù)據(jù)";
}
配置SpringDataUserDetailsService
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.yc.entity.UserInfo;
import com.yc.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
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.Service;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
@Service
public class SpringDataUserDetailsService implements UserDetailsService {
@Resource
private UserMapper userMapper;
@Autowired
PasswordEncoder passwordEncoder;
@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
if (s == null || "".equals(s)) {
throw new RuntimeException("用戶不能為空");
}
// 調(diào)用方法查詢用戶
UserInfo sysUser = userMapper.selectOne(
new QueryWrapper<UserInfo>().lambda().eq(UserInfo::getUsername, s));
List<SimpleGrantedAuthority> authorities = new ArrayList<>();
authorities.add(new SimpleGrantedAuthority("ROLE_FRONT_USER" ));
return new User(sysUser.getUsername(),sysUser.getPassword(), authorities);
}
授權(quán)服務(wù)器:AuthorizationServer
AuthorizationServer 需要繼承AuthorizationServerConfigurerAdapter
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.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
@Configuration
@EnableAuthorizationServer
public class AuthorizationServer extends AuthorizationServerConfigurerAdapter {
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
super.configure(clients);
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
super.configure(endpoints);
}
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
super.configure(security);
}
}
AuthorizationServerConfigurerAdapter源碼
- AuthorizationServerSecurityConfigurer:配置令牌端點(diǎn)(Token Endpoint)的安全約束
- ClientDetailsServiceConfigurer:配置OAuth2客戶端
- AuthorizationServerEndpointsConfigurer:配置授權(quán)(authorization)以及令牌(token)的訪問端點(diǎn)和令牌服務(wù)(token services)
配置客戶端詳細(xì)信息
ClientDetailsServiceConfigurer 能夠使用內(nèi)存或者JDBC來實(shí)現(xiàn)客戶端詳情服務(wù)(ClientDetailsService),
ClientDetailsService負(fù)責(zé)查找ClientDetails,而ClientDetails有幾個(gè)重要的屬性如下列表
- clientId:(必須的)用來標(biāo)識(shí)客戶的Id。
- secret:(需要值得信任的客戶端)客戶端安全碼,如果有的話
- scope:用來限制客戶端的訪問范圍,如果為空(默認(rèn))的話,那么客戶端擁有全部的訪問范圍。
- authorizedGrantTypes:此客戶端可以使用的授權(quán)類型,默認(rèn)為空。
- authorities:此客戶端可以使用的權(quán)限(基于Spring Security authorities)。
客戶端詳情(Client Details)能夠在應(yīng)用程序運(yùn)行的時(shí)候進(jìn)行更新,可以通過訪問底層的存儲(chǔ)服務(wù)(例如將客戶
端詳情存儲(chǔ)在一個(gè)關(guān)系數(shù)據(jù)庫的表中,就可以使用 JdbcClientDetailsService)或者通過自己實(shí)現(xiàn)
ClientRegistrationService接口(同時(shí)你也可以實(shí)現(xiàn) ClientDetailsService 接口)來進(jìn)行管理。
我們暫時(shí)使用內(nèi)存方式存儲(chǔ)客戶端詳情信息,配置如下:
// clients.withClientDetails(clientDetailsService);
clients.inMemory()// 使用in‐memory存儲(chǔ)
.withClient("c1")// client_id
.secret(new BCryptPasswordEncoder().encode("secret"))
//資源服務(wù)id
.resourceIds("res1")
// 該client允許的授權(quán)類型 authorization_code,password,refresh_token,implicit,client_credentials
.authorizedGrantTypes("authorization_code", "password", "client_credentials", "implicit", "refresh_token")
.scopes("all")// 允許的授權(quán)范圍 是一個(gè)標(biāo)識(shí)
.autoApprove(false)//false代表如果是授權(quán)碼模式 就跳轉(zhuǎn)到授權(quán)的頁面.
//加上驗(yàn)證回調(diào)地址
.redirectUris("http://www.baidu.com");
管理令牌
AuthorizationServerTokenServices 接口定義了一些操作使得你可以對(duì)令牌進(jìn)行一些必要的管理,令牌可以被用來
加載身份信息,里面包含了這個(gè)令牌的相關(guān)權(quán)限。
自己可以創(chuàng)建 AuthorizationServerTokenServices 這個(gè)接口的實(shí)現(xiàn),則需要繼承 DefaultTokenServices 這個(gè)類,
里面包含了一些有用實(shí)現(xiàn),你可以使用它來修改令牌的格式和令牌的存儲(chǔ)。默認(rèn)的,當(dāng)它嘗試創(chuàng)建一個(gè)令牌的時(shí)
候,是使用隨機(jī)值來進(jìn)行填充的,除了持久化令牌是委托一個(gè) TokenStore 接口來實(shí)現(xiàn)以外,這個(gè)類幾乎幫你做了
所有的事情。并且 TokenStore 這個(gè)接口有一個(gè)默認(rèn)的實(shí)現(xiàn),它就是 InMemoryTokenStore ,如其命名,所有的
令牌是被保存在了內(nèi)存中。除了使用這個(gè)類以外,你還可以使用一些其他的預(yù)定義實(shí)現(xiàn),下面有幾個(gè)版本,它們都實(shí)現(xiàn)了TokenStore 接口:
- InMemoryTokenStore:這個(gè)版本的實(shí)現(xiàn)是被默認(rèn)采用的,它可以完美的工作在單服務(wù)器上(即訪問并發(fā)量
壓力不大的情況下,并且它在失敗的時(shí)候不會(huì)進(jìn)行備份),大多數(shù)的項(xiàng)目都可以使用這個(gè)版本的實(shí)現(xiàn)來進(jìn)行
嘗試,你可以在開發(fā)的時(shí)候使用它來進(jìn)行管理,因?yàn)椴粫?huì)被保存到磁盤中,所以更易于調(diào)試。 - JdbcTokenStore:這是一個(gè)基于JDBC的實(shí)現(xiàn)版本,令牌會(huì)被保存進(jìn)關(guān)系型數(shù)據(jù)庫。使用這個(gè)版本的實(shí)現(xiàn)時(shí),
你可以在不同的服務(wù)器之間共享令牌信息,使用這個(gè)版本的時(shí)候請(qǐng)注意把"spring-jdbc"這個(gè)依賴加入到你的
classpath當(dāng)中。 - JwtTokenStore:這個(gè)版本的全稱是 JSON Web Token(JWT),它可以把令牌相關(guān)的數(shù)據(jù)進(jìn)行編碼(因此對(duì)
于后端服務(wù)來說,它不需要進(jìn)行存儲(chǔ),這將是一個(gè)重大優(yōu)勢(shì)),但是它有一個(gè)缺點(diǎn),那就是撤銷一個(gè)已經(jīng)授
權(quán)令牌將會(huì)非常困難,所以它通常用來處理一個(gè)生命周期較短的令牌以及撤銷刷新令牌(refresh_token)。
另外一個(gè)缺點(diǎn)就是這個(gè)令牌占用的空間會(huì)比較大,如果你加入了比較多用戶憑證信息。JwtTokenStore 不會(huì)保存任何數(shù)據(jù),但是它在轉(zhuǎn)換令牌值以及授權(quán)信息方面與 DefaultTokenServices 所扮演的角色是一樣的。
定義TokenConfig
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore;
/**
*
* 令牌存儲(chǔ)策略
*/
@Configuration
public class TokenConfig {
@Bean
public TokenStore tokenStore() {
//內(nèi)存模式
return new InMemoryTokenStore();
}
}
定義AuthorizationServerTokenServices
//令牌服務(wù)
@Autowired
private TokenStore tokenStore;
//客戶端詳情服務(wù)
@Autowired
private ClientDetailsService clientDetailsService;
/**
*
* 配置令牌服務(wù)
* 不管是什么模式都需要配置
* 需要使用到客戶端clientDetailsService和令牌服務(wù)tokenStore
*/
@Bean
public AuthorizationServerTokenServices tokenService() {
DefaultTokenServices service=new DefaultTokenServices();
//客戶端詳情
service.setClientDetailsService(clientDetailsService);
//刷新token
service.setSupportRefreshToken(true);
//令牌服務(wù)
service.setTokenStore(tokenStore);
// 令牌默認(rèn)有效期2小時(shí)
service.setAccessTokenValiditySeconds(7200);
// 刷新令牌默認(rèn)有效期3天
service.setRefreshTokenValiditySeconds(259200);
return service;
}
令牌訪問端點(diǎn)配置
AuthorizationServerEndpointsConfigurer 這個(gè)對(duì)象的實(shí)例可以完成令牌服務(wù)以及令牌endpoint配置。
配置授權(quán)類型(Grant Types)
AuthorizationServerEndpointsConfigurer 通過設(shè)定以下屬性決定支持的授權(quán)類型(Grant Types):
-
authenticationManager:認(rèn)證管理器,當(dāng)你選擇了資源所有者密碼(password)授權(quán)類型的時(shí)候,請(qǐng)?jiān)O(shè)置
這個(gè)屬性注入一個(gè) AuthenticationManager 對(duì)象。 -
userDetailsService:如果你設(shè)置了這個(gè)屬性的話,那說明你有一個(gè)自己的 UserDetailsService 接口的實(shí)現(xiàn), 或者你可以把這個(gè)東西設(shè)置到全局域上面去(例如 GlobalAuthenticationManagerConfigurer 這個(gè)配置對(duì)
象),當(dāng)你設(shè)置了這個(gè)之后,那么 “refresh_token” 即刷新令牌授權(quán)類型模式的流程中就會(huì)包含一個(gè)檢查,用
來確保這個(gè)賬號(hào)是否仍然有效,假如說你禁用了這個(gè)賬戶的話。 - authorizationCodeServices:這個(gè)屬性是用來設(shè)置授權(quán)碼服務(wù)的(即 AuthorizationCodeServices 的實(shí)例對(duì) 象),主要用于 “authorization_code” 授權(quán)碼類型模式。
- implicitGrantService:這個(gè)屬性用于設(shè)置隱式授權(quán)模式,用來管理隱式授權(quán)模式的狀態(tài)。不常用
- tokenGranter:當(dāng)你設(shè)置了這個(gè)東西(即 TokenGranter 接口實(shí)現(xiàn)),那么授權(quán)將會(huì)交由你來完全掌控,并
且會(huì)忽略掉上面的這幾個(gè)屬性,這個(gè)屬性一般是用作拓展用途的,即標(biāo)準(zhǔn)的四種授權(quán)模式已經(jīng)滿足不了你的
需求的時(shí)候,才會(huì)考慮使用這個(gè)
配置授權(quán)端點(diǎn)的URL(Endpoint URLs):
AuthorizationServerEndpointsConfigurer 這個(gè)配置對(duì)象有一個(gè)叫做 pathMapping() 的方法用來配置端點(diǎn)URL鏈
接,它有兩個(gè)參數(shù):
- 第一個(gè)參數(shù):String 類型的,這個(gè)端點(diǎn)URL的默認(rèn)鏈接。
- 第二個(gè)參數(shù):String 類型的,你要進(jìn)行替代的URL鏈接。
以上的參數(shù)都將以 “/” 字符為開始的字符串,框架的默認(rèn)URL鏈接如下列表,可以作為這個(gè) pathMapping() 方法的 第一個(gè)參數(shù): - /oauth/authorize:授權(quán)端點(diǎn)。
- /oauth/token:令牌端點(diǎn)。
- /oauth/confirm_access:用戶確認(rèn)授權(quán)提交端點(diǎn)。
- /oauth/error:授權(quán)服務(wù)錯(cuò)誤信息端點(diǎn)。
- /oauth/check_token:用于資源服務(wù)訪問的令牌解析端點(diǎn)。
- /oauth/token_key:提供公有密匙的端點(diǎn),如果你使用JWT令牌的話。
需要注意的是授權(quán)端點(diǎn)這個(gè)URL應(yīng)該被Spring Security保護(hù)起來只供授權(quán)用戶訪問.
在AuthorizationServer配置令牌訪問端點(diǎn)
@Bean
public AuthorizationCodeServices authorizationCodeServices() { //設(shè)置授權(quán)碼模式的授權(quán)碼如何
存取,暫時(shí)采用內(nèi)存方式
return new InMemoryAuthorizationCodeServices();
}
@Autowired
private AuthorizationCodeServices authorizationCodeServices;
@Autowired
private AuthenticationManager authenticationManager;
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
endpoints
//密碼模式所需要的
.authenticationManager(authenticationManager)
//授權(quán)碼模式所需要的
.authorizationCodeServices(authorizationCodeServices)
//如果你設(shè)置了這個(gè)屬性的話,那說明你有一個(gè)自己的 UserDetailsService 接口的實(shí)現(xiàn)
//.userDetailsService(null)
//令牌管理服務(wù)
.tokenServices(tokenService())
//自定義令牌端點(diǎn)URL
// .pathMapping("/oauth/token","/user/token")
//自定義授權(quán)端點(diǎn)URL
// .pathMapping("/oauth/authorize","/user/authorize")
//允許POST提交
.allowedTokenEndpointRequestMethods(HttpMethod.POST);
}
令牌端點(diǎn)的安全約束
AuthorizationServerSecurityConfigurer用來配置令牌端點(diǎn)(Token Endpoint)的安全約束,在
AuthorizationServer中配置如下
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security
//tokenkey這個(gè)endpoint當(dāng)使用JwtToken且使用非對(duì)稱加密時(shí),資源服務(wù)用于獲取公鑰而開放的,這里指這個(gè)endpoint完全公開。
.tokenKeyAccess("permitAll()")
//checkToken這個(gè)endpoint完全公開
.checkTokenAccess("permitAll()")
//允許表單認(rèn)證
.allowFormAuthenticationForClients() ;
}
- tokenkey這個(gè)endpoint當(dāng)使用JwtToken且使用非對(duì)稱加密時(shí),資源服務(wù)用于獲取公鑰而開放的,這里指這個(gè) endpoint完全公開。
- checkToken這個(gè)endpoint完全公開
- 允許表單認(rèn)證
web安全配置
@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
//安全攔截機(jī)制(最重要)
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/r/**").authenticated()//所有/r/**的請(qǐng)求必須認(rèn)證通過
.anyRequest().permitAll()//除了/r/**,其它的請(qǐng)求可以訪問
.and()
.formLogin()//允許表單登錄
.loginPage("/login-view")//登錄頁面
.loginProcessingUrl("/login")
.successForwardUrl("/login-success")//自定義登錄成功的頁面地址
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
.and()
.logout()
.logoutUrl("/logout")
.logoutSuccessUrl("/login-view?logout");
}
/**
*
* oauth2.0 密碼模式下需要認(rèn)證管理器
* 密碼模式所需要的
*
* @return
* @throws Exception
*/
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception
{
return super.authenticationManagerBean();
}
}
授權(quán)碼模式
獲取授權(quán)碼
瀏覽器訪問
http://localhost:8081/oauth/authorize?client_id=c1&response_type=code&scope=all&redirect_uri=http://www.baidu.com
會(huì)跳轉(zhuǎn)到登錄頁面
輸入賬號(hào)admin 密碼123
點(diǎn)擊授權(quán)
得到授權(quán)碼
客戶端拿著授權(quán)碼向授權(quán)服務(wù)器索要訪問access_token
http://localhost:8081/oauth/token
參數(shù)
- code : 剛剛獲取的授權(quán)碼
- client_id :c1
- client_secret :secret
- grant_type : authorization_code 授權(quán)碼模式
- redirect_uri :http://www.baidu.com
簡(jiǎn)化模式(直接獲取token)
參數(shù)描述同授權(quán)碼模式 ,注意response_type=token,說明是簡(jiǎn)化模式。
一般來說,簡(jiǎn)化模式用于沒有服務(wù)器端的第三方單頁面應(yīng)用,因?yàn)闆]有服務(wù)器端就無法接收授權(quán)碼。
http://localhost:8081/oauth/authorize?client_id=c1&response_type=token&scope=all&redirect_uri=http://www.baidu.com
密碼模式獲取token
參數(shù)列表如下:
client_id:客戶端準(zhǔn)入標(biāo)識(shí)。
client_secret:客戶端秘鑰。
grant_type:授權(quán)類型,填寫password表示密碼模式
username:資源擁有者用戶名。
password:資源擁有者密碼
這種模式十分簡(jiǎn)單,但是卻意味著直接將用戶敏感信息泄漏給了client,因此這就說明這種模式只能用于client是我
們自己開發(fā)的情況下。因此密碼模式一般用于我們自己開發(fā)的,第一方原生App或第一方單頁面應(yīng)用。
客戶端模式
參數(shù)列表如下
client_id:客戶端準(zhǔn)入標(biāo)識(shí)。
client_secret:客戶端秘鑰。
grant_type:授權(quán)類型,填寫client_credentials表示客戶端模式
這種模式是最方便但最不安全的模式。因此這就要求我們對(duì)client完全的信任,而client本身也是安全的。因
此這種模式一般用來提供給我們完全信任的服務(wù)器端服務(wù)。比如,合作方系統(tǒng)對(duì)接,拉取一組用戶信息。
搭建資源服務(wù)器
添加依賴
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<java.version>1.8</java.version>
<druid.version>1.1.14</druid.version>
<fastjson.version>1.2.68</fastjson.version>
<security.oauth2.version>2.3.5.RELEASE</security.oauth2.version>
</properties>
<dependencies>
<!-- SpringBoot 核心包 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- SpringBoot 測(cè)試 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- SpringBoot Web容器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- spring-boot-devtools -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional> <!-- 表示依賴不會(huì)傳遞 -->
</dependency>
<!-- spring security 安全認(rèn)證 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!--若依使用的 spring security oauth2 開放授權(quán) -->
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<version>${security.oauth2.version}</version>
</dependency>
<!--常用工具類 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<!-- 阿里JSON解析器 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
<!--Spring框架基本的核心工具-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
</dependency>
<!-- jwt-->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-jwt</artifactId>
<version>1.1.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.26</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
資源服務(wù)器配置
@EnableResourceServer 注解到一個(gè) @Configuration 配置類上,并且必須使用 ResourceServerConfigurer 這個(gè)
配置對(duì)象來進(jìn)行配置(可以選擇繼承自 ResourceServerConfigurerAdapter 然后覆寫其中的方法,參數(shù)就是這個(gè)
對(duì)象的實(shí)例),下面是一些可以配置的屬性:
ResourceServerSecurityConfigurer中主要包括:
- tokenServices:ResourceServerTokenServices 類的實(shí)例,用來實(shí)現(xiàn)令牌服務(wù)。
- tokenStore:TokenStore類的實(shí)例,指定令牌如何訪問,與tokenServices配置可選
- resourceId:這個(gè)資源服務(wù)的ID,這個(gè)屬性是可選的,但是推薦設(shè)置并在授權(quán)服務(wù)中進(jìn)行驗(yàn)證。
- 其他的拓展屬性例如 tokenExtractor 令牌提取器用來提取請(qǐng)求中的令牌。
HttpSecurity配置這個(gè)與Spring Security類似: - 請(qǐng)求匹配器,用來設(shè)置需要進(jìn)行保護(hù)的資源路徑,默認(rèn)的情況下是保護(hù)資源服務(wù)的全部路徑。
- 通過http.authorizeRequests()來設(shè)置受保護(hù)資源的訪問規(guī)則
- 其他的自定義權(quán)限保護(hù)規(guī)則通過 HttpSecurity 來進(jìn)行配置。
- @EnableResourceServer 注解自動(dòng)增加了一個(gè)類型為 OAuth2AuthenticationProcessingFilter 的過濾器鏈
編寫ResouceServerConfig:
ResouceServerConfig
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.http.SessionCreationPolicy;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.RemoteTokenServices;
import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices;
@Configuration
@EnableResourceServer
public class ResouceServerConfig extends
ResourceServerConfigurerAdapter {
//授權(quán)服務(wù)器配置的資源id 保持一致
public static final String RESOURCE_ID = "res1";
@Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources.resourceId(RESOURCE_ID)
.tokenServices(tokenService())
.stateless(true);
}
@Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
//授權(quán)服務(wù)器配置的允許的授權(quán)范圍 是一個(gè)標(biāo)識(shí) 保持一致
.antMatchers("/**").access("#oauth2.hasScope('all')")
.and().csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
//資源服務(wù)令牌解析服務(wù)
@Bean
public ResourceServerTokenServices tokenService() {
//使用遠(yuǎn)程服務(wù)請(qǐng)求授權(quán)服務(wù)器校驗(yàn)token,必須指定校驗(yàn)token 的url、client_id,client_secret
RemoteTokenServices service=new RemoteTokenServices();
//授權(quán)服務(wù)器地址認(rèn)證token的
/**
* 在這里面配置的security //tokenkey這個(gè)endpoint當(dāng)使用JwtToken且使用非對(duì)稱加密時(shí),資源服務(wù)用于獲取公鑰而開放的,這里指這個(gè)endpoint完全公開。
.tokenKeyAccess("permitAll()")
//checkToken這個(gè)endpoint完全公開
.checkTokenAccess("permitAll()")
//允許表單認(rèn)證
.allowFormAuthenticationForClients() ;
**/
service.setCheckTokenEndpointUrl("http://localhost:8081/oauth/check_token");
//客戶端id
service.setClientId("c1");
//
service.setClientSecret("secret");
return service;
}
添加安全訪問控制
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
//安全攔截機(jī)制(最重要)
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/user/**").authenticated()//所有/user/**的請(qǐng)求必須認(rèn)證通過
//攔截其它的請(qǐng)求可以訪問
.anyRequest().permitAll();
}
}
編寫資源
@RestController
@RequestMapping(value = "/user")
public class UserController {
@GetMapping(value = "/test")
public String r1(){
return "訪問資源1";
}
}
獲取到token之后
測(cè)試訪問資源
完成訪問
JWT令牌
在授權(quán)服務(wù)器中更改原來TokenConfig
@Configuration
public class TokenConfig {
// @Bean
// public TokenStore tokenStore() {
// //內(nèi)存模式
// return new InMemoryTokenStore();
// }
/**
*
*/
private String SIGNING_KEY = "TOKEN_CAT";
@Bean
public TokenStore tokenStore() {
return new JwtTokenStore(accessTokenConverter());
}
@Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey(SIGNING_KEY); //對(duì)稱秘鑰,資源服務(wù)器使用該秘鑰來驗(yàn)證
return converter;
}
}
定義JWT令牌服務(wù)
**更改AuthorizationServer中的AuthorizationServerTokenServices **
@Autowired
private JwtAccessTokenConverter accessTokenConverter;
@Bean
public AuthorizationServerTokenServices tokenService() {
DefaultTokenServices service=new DefaultTokenServices();
//客戶端詳情
service.setClientDetailsService(clientDetailsService);
//刷新token
service.setSupportRefreshToken(true);
//令牌服務(wù)
service.setTokenStore(tokenStore);
新添加的代碼
TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
tokenEnhancerChain.setTokenEnhancers(Arrays.asList(accessTokenConverter));
service.setTokenEnhancer(tokenEnhancerChain);
新添加的代碼
// 令牌默認(rèn)有效期2小時(shí)
service.setAccessTokenValiditySeconds(7200);
// 刷新令牌默認(rèn)有效期3天
service.setRefreshTokenValiditySeconds(259200);
return service;
}
使用密碼模式測(cè)試生成令牌
校驗(yàn)jwt令牌
資源服務(wù)需要和授權(quán)服務(wù)擁有一致的簽字、令牌服務(wù)等
- 將授權(quán)服務(wù)中的TokenConfig類拷貝到資源 服務(wù)中
@Configuration
public class TokenConfig {
// @Bean
// public TokenStore tokenStore() {
// //內(nèi)存模式
// return new InMemoryTokenStore();
// }
/**
*
*/
private String SIGNING_KEY = "TOKEN_CAT";
@Bean
public TokenStore tokenStore() {
return new JwtTokenStore(accessTokenConverter());
}
@Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey(SIGNING_KEY); //對(duì)稱秘鑰,資源服務(wù)器使用該秘鑰來驗(yàn)證
return converter;
}
- 屏蔽資源 服務(wù)原來的令牌服務(wù)類
更改 ResouceServerConfig配置類
注入TokenStore
@Autowired
TokenStore tokenStore;
@Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources.resourceId(RESOURCE_ID)
// .tokenServices(tokenService())
//使用自己配置的令牌服務(wù)
.tokenStore( tokenStore)
.stateless(true);
}
測(cè)試驗(yàn)證token
完善環(huán)境配置
截止目前客戶端信息和授權(quán)碼仍然存儲(chǔ)在內(nèi)存中,生產(chǎn)環(huán)境中通過會(huì)存儲(chǔ)在數(shù)據(jù)庫中,下邊完善環(huán)境的配置
創(chuàng)建表
CREATE TABLE `oauth_client_details` (
`client_id` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL COMMENT '客戶端標(biāo)\r\n識(shí)',
`resource_ids` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL COMMENT '接入資源列表',
`client_secret` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL COMMENT '客戶端秘鑰',
`scope` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL,
`authorized_grant_types` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL,
`web_server_redirect_uri` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL,
`authorities` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL,
`access_token_validity` int DEFAULT NULL,
`refresh_token_validity` int DEFAULT NULL,
`additional_information` longtext CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci,
`create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`archived` tinyint DEFAULT NULL,
`trusted` tinyint DEFAULT NULL,
`autoapprove` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL,
PRIMARY KEY (`client_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COMMENT='接入客戶端信息';
DROP TABLE IF EXISTS `oauth_code`;
CREATE TABLE `oauth_code` (
`create_time` timestamp(0) NOT NULL DEFAULT CURRENT_TIMESTAMP,
`code` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`authentication` blob NULL,
INDEX `code_index`(`code`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
插入數(shù)據(jù)
INSERT INTO `security_oauth_demo`.`oauth_client_details`(`client_id`, `resource_ids`, `client_secret`, `scope`, `authorized_grant_types`, `web_server_redirect_uri`, `authorities`, `access_token_validity`, `refresh_token_validity`, `additional_information`, `create_time`, `archived`, `trusted`, `autoapprove`) VALUES ('c1', 'res1', '$2a$10$3CCcmHbkYEz55brA3BL6J.B7VW4HxsnkYmJ/Yz95f0Gvkpnammpoi', 'ROLE_ADMIN,ROLE_USER,ROLE_API', 'client_credentials,password,authorization_code,implicit,refresh_token', 'http://www.baidu.com', NULL, 7200, 259200, NULL, '2023-10-19 09:51:20', 0, 0, 'false');
INSERT INTO `security_oauth_demo`.`oauth_client_details`(`client_id`, `resource_ids`, `client_secret`, `scope`, `authorized_grant_types`, `web_server_redirect_uri`, `authorities`, `access_token_validity`, `refresh_token_validity`, `additional_information`, `create_time`, `archived`, `trusted`, `autoapprove`) VALUES ('c2', 'res1', '$2a$10$3CCcmHbkYEz55brA3BL6J.B7VW4HxsnkYmJ/Yz95f0Gvkpnammpoi', 'ROLE_ADMIN,ROLE_USER,ROLE_API', 'client_credentials,password,authorization_code,implicit,refresh_token', 'http://www.baidu.com', NULL, 7200, 259200, NULL, '2023-10-19 09:51:20', 0, 0, 'false');
配置授權(quán)服務(wù)
修改AuthorizationServer:
ClientDetailsService和AuthorizationCodeServices從數(shù)據(jù)庫讀取數(shù)據(jù)。
/**
* 1.客戶端詳情相關(guān)配置
*/
注入ClientDetailsService
/**
* 客戶端認(rèn)證服務(wù) 因?yàn)橹笆褂玫氖莾?nèi)存模式 直接配置信息就好了
* 現(xiàn)在使用的是數(shù)據(jù)庫 所以要配置數(shù)據(jù)庫信息
* @param dataSource
* @return
*/
@Bean
public ClientDetailsService clientDetailsService(DataSource dataSource) {
ClientDetailsService clientDetailsService = new JdbcClientDetailsService(dataSource);
((JdbcClientDetailsService) clientDetailsService).setPasswordEncoder( new BCryptPasswordEncoder() );
return clientDetailsService;
}
客戶端配置使用剛剛的數(shù)據(jù)庫模式 clientDetailsService
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.withClientDetails(clientDetailsService);
// clients.inMemory()// 使用in‐memory存儲(chǔ)
// .withClient("c1")// client_id
// .secret(new BCryptPasswordEncoder().encode("secret"))
// //資源服務(wù)id
// .resourceIds("res1")
// // 該client允許的授權(quán)類型 authorization_code,password,refresh_token,implicit,client_credentials
// .authorizedGrantTypes("authorization_code", "password", "client_credentials", "implicit", "refresh_token")
// .scopes("all")// 允許的授權(quán)范圍 是一個(gè)標(biāo)識(shí)
// .autoApprove(false)//false代表如果是授權(quán)碼模式 就跳轉(zhuǎn)到授權(quán)的頁面.
// //加上驗(yàn)證回調(diào)地址
// .redirectUris("http://www.baidu.com");
}
配置授權(quán)碼模式也使用數(shù)據(jù)庫
原來的注釋掉
// @Bean
// public AuthorizationCodeServices authorizationCodeServices() {
// //設(shè)置授權(quán)碼模式的授權(quán)碼如何 存取,暫時(shí)采用內(nèi)存方式
// return new InMemoryAuthorizationCodeServices();
// }
//使用DataSource 模式
@Bean
public AuthorizationCodeServices authorizationCodeServices(DataSource dataSource) {
return new JdbcAuthorizationCodeServices(dataSource);//設(shè)置授權(quán)碼模式的授權(quán)碼如何存取
}
首先使用密碼模式獲取token
其中oauth_client_details表中client_secret客戶端密鑰字段 是secret使用new BCryptPasswordEncoder() 加密模式進(jìn)行加密的,如果不能使用自行進(jìn)行加密
使用授權(quán)碼模式獲取token
http://localhost:8081/oauth/authorize?client_id=c1&response_type=code&scope=all&redirect_uri=http://www.baidu.com
獲取異常,錯(cuò)誤信息如下
Resolved [error=“invalid_scope”, error_description=“Invalid scope: all”, scope=“ROLE_ADMIN ROLE_USER ROLE_API”]
意思是scope傳入的是all,而數(shù)據(jù)庫是這些ROLE_ADMIN ROLE_USER ROLE_API
更改scope為其中一個(gè)再次進(jìn)行測(cè)試
http://localhost:8081/oauth/authorize?client_id=c1&response_type=code&scope=ROLE_API&redirect_uri=http://www.baidu.com
獲取到授權(quán)碼
上面是授權(quán)服務(wù)器和認(rèn)證服務(wù)器分開的.現(xiàn)在準(zhǔn)備整合在一起
在授權(quán)服務(wù)器添加資源服務(wù)器ResouceServerConfig
import com.yc.config.handler.CustomAccessDeniedHandler;
import com.yc.config.handler.CustomAuthExceptionEntryPoint;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.TokenStore;
@Configuration
@EnableResourceServer
public class ResouceServerConfig extends
ResourceServerConfigurerAdapter {
public static final String RESOURCE_ID = "res1";
@Autowired
TokenStore tokenStore;
/**
* 自定義訪問無權(quán)限資源時(shí)的異常
*/
@Autowired
private CustomAccessDeniedHandler accessDeniedHandler;
/**
* 自定義認(rèn)證失敗的異常
*/
@Autowired
private CustomAuthExceptionEntryPoint exceptionEntryPoint;
@Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources
//必須擁有RESOURCE_ID 配置, 如果不使用克注釋掉
.resourceId(RESOURCE_ID)
//使用自己配置的令牌服務(wù)
.tokenStore( tokenStore)
// 自定義認(rèn)證失敗的異常 自定義訪問無權(quán)限資源時(shí)的異常
.authenticationEntryPoint(exceptionEntryPoint).accessDeniedHandler(accessDeniedHandler)
.stateless(true);
}
@Override
public void configure(HttpSecurity http) throws Exception {
/**
* 攔截所有請(qǐng)求,同時(shí)scopes必須使用all的 如果不需要可以使用下面的注釋掉,或者使用下面的認(rèn)證配置
* .antMatchers("/**").access("#oauth2.hasScope('all')")
*/
http
.authorizeRequests()
.antMatchers("/**")
.access("#oauth2.hasScope('all')")
.and().csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
/**
*
*/
// 所有請(qǐng)求必須認(rèn)證通過
// http.authorizeRequests()
// // 過濾放行
// .antMatchers().permitAll()
// .anyRequest().authenticated();
}
}
CustomAccessDeniedHandler文章來源:http://www.zghlxwxcb.cn/news/detail-723317.html
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* 自定義訪問無權(quán)限資源時(shí)的異常
*
* @author
*/
@Component
public class CustomAccessDeniedHandler implements AccessDeniedHandler
{
@Override
public void handle(HttpServletRequest request, HttpServletResponse response,
AccessDeniedException accessDeniedException) throws IOException
{
System.out.println("權(quán)限不足,請(qǐng)聯(lián)系管理員");
System.out.println("權(quán)限不足,請(qǐng)聯(lián)系管理員");
System.out.println("權(quán)限不足,請(qǐng)聯(lián)系管理員");
System.out.println("權(quán)限不足,請(qǐng)聯(lián)系管理員");
System.out.println("權(quán)限不足,請(qǐng)聯(lián)系管理員");
System.out.println("權(quán)限不足,請(qǐng)聯(lián)系管理員");
System.out.println("權(quán)限不足,請(qǐng)聯(lián)系管理員");
System.out.println("權(quán)限不足,請(qǐng)聯(lián)系管理員");
System.out.println("權(quán)限不足,請(qǐng)聯(lián)系管理員");
System.out.println("權(quán)限不足,請(qǐng)聯(lián)系管理員");
System.out.println("權(quán)限不足,請(qǐng)聯(lián)系管理員");
System.out.println("權(quán)限不足,請(qǐng)聯(lián)系管理員");
System.out.println("權(quán)限不足,請(qǐng)聯(lián)系管理員");
response.setStatus(200);
response.setContentType("application/json");
response.setCharacterEncoding("utf-8");
response.getWriter().print("權(quán)限不足,請(qǐng)聯(lián)系管理員");
}
}
CustomAuthExceptionEntryPoint文章來源地址http://www.zghlxwxcb.cn/news/detail-723317.html
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* 自定義認(rèn)證失敗的異常
*
* @author
*/
@Component
public class CustomAuthExceptionEntryPoint implements AuthenticationEntryPoint
{
@Override
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException
{
System.out.println("令牌不合法,禁止訪問");
System.out.println("令牌不合法,禁止訪問");
System.out.println("令牌不合法,禁止訪問");
System.out.println("令牌不合法,禁止訪問");
System.out.println("令牌不合法,禁止訪問");
System.out.println("令牌不合法,禁止訪問");
System.out.println("令牌不合法,禁止訪問");
System.out.println("令牌不合法,禁止訪問");
System.out.println("令牌不合法,禁止訪問");
System.out.println("令牌不合法,禁止訪問");
System.out.println("令牌不合法,禁止訪問");
System.out.println("令牌不合法,禁止訪問");
System.out.println("令牌不合法,禁止訪問");
System.out.println("令牌不合法,禁止訪問");
System.out.println("令牌不合法,禁止訪問");
response.setStatus(200);
response.setContentType("application/json");
response.setCharacterEncoding("utf-8");
response.getWriter().print("令牌不合法,禁止訪問");
}
}
到此結(jié)束
到了這里,關(guān)于SpringSecurity+ Oauth2.0+JWT 0-1的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!