Oauth2.0提供了四種認(rèn)證方式:
授權(quán)碼模式(authorization code)、
簡(jiǎn)化模式(implicit)、
密碼模式(resource owner passwordcredentials)、
客戶端模式(client credentials)
Oauth2.0中的四個(gè)重要角色
OAauth2.0包括的角色 | 說(shuō)明 |
資源擁有者 | 通常為用戶,也可以是應(yīng)用程序,即該資源的擁有者。 |
客戶端 | 本身不存儲(chǔ)資源,需要通過(guò)資源擁有者的授權(quán)去請(qǐng)求資源服務(wù)器的資源,比如:Android客戶端、Web客戶端(瀏覽器端)、微信客戶端等。 |
授權(quán)服務(wù)器(也稱認(rèn)證服務(wù)器) | 用于服務(wù)提供商對(duì)資源擁有者的身份進(jìn)行認(rèn)證、對(duì)訪問(wèn)資源進(jìn)行授權(quán),認(rèn)證成功后會(huì)給客戶端發(fā)放令牌 (access_token),作為客戶端訪問(wèn)資源服務(wù)器的憑據(jù)。 |
資源服務(wù)器 | 存儲(chǔ)資源的服務(wù)器。 |
廢話不多說(shuō),直接上代碼。
引入依賴
<!-- springSecurity-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- Oauth2-->
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<version>2.2.6.RELEASE</version>
</dependency>
<!-- jwt增強(qiáng)-->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-jwt</artifactId>
<version>1.1.0.RELEASE</version>
</dependency>
添加配置文件
SpringSecurity配置文件
import com.municipal.service.impl.UserAuthService;
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.config.annotation.authentication.builders.AuthenticationManagerBuilder;
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;
/**
* @Author majinzhong
* @Date 2023/6/12 19:11
* @Version 1.0
* URI攔截.
*/
@EnableWebSecurity
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserAuthService userAuthService;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/oauth/**", "/oauth2/**","/login/**", "logout/**").permitAll() // login 頁(yè)面,所有用戶都可以訪問(wèn)
//.antMatchers("/home").hasAnyRole("USER", "com.municipal.pojo") // home 頁(yè)面,ADMIN 和 USER 都可以訪問(wèn)
.anyRequest().authenticated()
// .and()
// .formLogin()
// .loginPage("/login") // 自定義登錄表單
//.defaultSuccessUrl("/home", true) // 登錄成功跳轉(zhuǎn)的頁(yè)面,第二個(gè)參數(shù)true表示每次登錄成功都是跳轉(zhuǎn)到home,如果false則表示跳轉(zhuǎn)到登錄之前訪問(wèn)的頁(yè)面
//.failureUrl("/login?error=true")
// .failureHandler(authenticationFailureHandler()) // 失敗跳轉(zhuǎn)的頁(yè)面(比如用戶名/密碼錯(cuò)誤),這里還是跳轉(zhuǎn)到login頁(yè)面,只是給出錯(cuò)誤提示
.and().logout().permitAll() // 登出 所有用戶都可以訪問(wèn)
.and().csrf().disable(); // 關(guān)閉csrf,此時(shí)登出logout接收任何形式的請(qǐng)求;(默認(rèn)開(kāi)啟,logout只接受post請(qǐng)求)
}
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
//設(shè)置登陸賬戶和密碼
// BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
// auth.inMemoryAuthentication().withUser("admin").
// password(encoder.encode("123456")).authorities("ROLE ADMIN");
//從數(shù)據(jù)庫(kù)中讀取賬戶密碼
auth.userDetailsService(userAuthService);
}
//---------------------
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
Oauth配置文件
import com.municipal.service.impl.UserAuthService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
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.error.WebResponseExceptionTranslator;
import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore;
import javax.annotation.Resource;
import javax.sql.DataSource;
/**
* 認(rèn)證服務(wù)端配置.
* @Author majinzhong
* @Date 2023/7/5 17:24
* @Version 1.0
*/
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
@Resource
AuthenticationManager authenticationManager;
//
// @Resource
// RedisConnectionFactory redisConnectionFactory;
@Autowired
private CustomWebResponseExceptionTranslator customWebResponseExceptionTranslator;
@Autowired
UserAuthService userAuthService;
@Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Resource
DataSource dataSource;
@Override
public void configure(ClientDetailsServiceConfigurer clientDetailsServiceConfigurer) throws Exception {
// 內(nèi)存存儲(chǔ)配置信息+密碼模式
serviceConfig(0, clientDetailsServiceConfigurer, dataSource, "password");
}
/**
* 服務(wù)配置,如授權(quán)方式,token過(guò)期時(shí)間等.
*
* @param flag 內(nèi)存和數(shù)據(jù)庫(kù)標(biāo)識(shí),0:內(nèi)存;1:數(shù)據(jù)庫(kù)
* @param clientDetailsServiceConfigurer 配置器
* @param dataSource 數(shù)據(jù)源
* @param grantType 授權(quán)類型,password:密碼模式;authorization_code:授權(quán)碼模式
* @throws Exception 異常
*/
private void serviceConfig(int flag, ClientDetailsServiceConfigurer clientDetailsServiceConfigurer, DataSource dataSource, String grantType) throws Exception {
// if (flag == 1) {
// clientDetailsServiceConfigurer.jdbc(dataSource);
// } else {
clientDetailsServiceConfigurer
//存儲(chǔ)到內(nèi)存中
.inMemory()
//配置client_id
.withClient("client")
//配置client-secret
.secret(new BCryptPasswordEncoder().encode("123456"))
//配置訪問(wèn)token的有效期
.accessTokenValiditySeconds(3600)
//配置刷新token的有效期
.refreshTokenValiditySeconds(864000)
//配置redirect_uri,用于授權(quán)成功后跳轉(zhuǎn)
// .redirectUris("http://www.baidu.com")
//配置申請(qǐng)的權(quán)限范圍
.scopes("all")
//配置grant_type,表示授權(quán)類型
.authorizedGrantTypes("password","refresh_token");
// }
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpointsConfig) throws Exception {
// 默認(rèn)情況下,授權(quán)碼存儲(chǔ)在內(nèi)存中:InMemoryAuthorizationCodeServices,
// 所以,不用配置
endpointsConfig.tokenStore(new InMemoryTokenStore()) //增加 TokenStore 配置
.authenticationManager(authenticationManager) //使用密碼模式需要配置
.allowedTokenEndpointRequestMethods(HttpMethod.GET,HttpMethod.POST) //支持GET,POST請(qǐng)求
.userDetailsService(userAuthService); //設(shè)置userDetailsService刷新token時(shí)候會(huì)用到
endpointsConfig.exceptionTranslator(customWebResponseExceptionTranslator);//錯(cuò)誤異常
}
// /**
// * 配置授權(quán)碼存儲(chǔ)位置.
// *
// * @param flag 授權(quán)碼存儲(chǔ)位置標(biāo)識(shí):0,內(nèi)存;1:數(shù)據(jù)庫(kù)
// * @param endpointsConfig 端配置
// * @param randomValueAuthorizationCodeServices 授權(quán)碼存儲(chǔ)位置
// */
// private void codeStoreEndpointConfig(int flag, AuthorizationServerEndpointsConfigurer endpointsConfig, RandomValueAuthorizationCodeServices randomValueAuthorizationCodeServices) {
// if (flag == 1) {
// // 授權(quán)碼存儲(chǔ)數(shù)據(jù)庫(kù),需要配置jdbc存儲(chǔ)
// endpointsConfig.authorizationCodeServices(randomValueAuthorizationCodeServices);
// }
// }
@Override
public void configure(AuthorizationServerSecurityConfigurer serverSecurityConfig) throws Exception {
//允許表單認(rèn)證
serverSecurityConfig.allowFormAuthenticationForClients();
//添加tokan校驗(yàn)失敗返回消息
serverSecurityConfig.authenticationEntryPoint(new AuthExceptionEntryPoint());
}
}
認(rèn)證資源配置
import com.municipal.service.impl.UserAuthService;
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.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;
/**
* 認(rèn)證資源配置.
* @Author majinzhong
* @Date 2023/7/5 17:29
* @Version 1.0
*/
@Configuration
@EnableResourceServer
public class ResourceConfig extends ResourceServerConfigurerAdapter {
// @Override
// public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
// resources.resourceId("admin");
// }
@Override
public void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.requestMatchers()
.antMatchers("/**")
.and()
.authorizeRequests()
.antMatchers("/**")
.authenticated();
}
}
配置UserDetailsService
import com.municipal.pojo.UserInfo;
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.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Component;
/**
* @Author majinzhong
* @Date 2023/6/29 10:51
* @Version 1.0
*/
@Component
public class UserAuthService implements UserDetailsService {
// @Autowired
// private UserMapper userMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// User user = userMapper.getByUsername(username);
// if(user == null){
// throw new UsernameNotFoundException("not found the user:"+username);
// }else{
// return user;
// }
if("admin".equals(username)){
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
return new UserInfo(1, "admin", encoder.encode("123456"));
}else{
throw new UsernameNotFoundException("not found the user:"+username);
}
}
}
用戶實(shí)體類
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.ArrayList;
import java.util.Collection;
/**
* @Author majinzhong
* @Date 2023/6/29 10:49
* @Version 1.0
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserInfo implements UserDetails {
private Integer userId;
private String userName;
private String password;
//將我們的 權(quán)限字符串進(jìn)行分割,并存到集合中,最后供 AuthenticationProvider 使用
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return null;
}
//將我們的 密碼 轉(zhuǎn)換成 AuthenticationProvider 能夠認(rèn)識(shí)的 password
@Override
public String getPassword() {
return this.password;
}
//將我們的 賬戶 轉(zhuǎn)換成 AuthenticationProvider 能夠認(rèn)識(shí)的 username
@Override
public String getUsername() {
return this.userName;
}
//都返回 true
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
至此,OAuth已經(jīng)配置成功了,但當(dāng)瀏覽器訪問(wèn)/oauth/token接口返回401的時(shí)候,頁(yè)面會(huì)出現(xiàn)彈框
是因?yàn)閞esponse headers里面有Www-Authenticate: Basic realm="oauth2/client"
為了把這個(gè)彈框去掉也是費(fèi)了一番大功夫
密碼模式錯(cuò)誤處理類(WebResponseExceptionTranslator)
import org.springframework.http.ResponseEntity;
import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
import org.springframework.security.oauth2.provider.error.WebResponseExceptionTranslator;
import org.springframework.stereotype.Component;
/**
*
* @ClassName: CustomWebResponseExceptionTranslator
* @Description:password模式錯(cuò)誤處理,自定義登錄失敗異常信息
* @Author majinzhong
* @Date 2023/7/3 16:33
* @Version 1.0
*/
@Component
public class CustomWebResponseExceptionTranslator implements WebResponseExceptionTranslator {
@Override
public ResponseEntity<OAuth2Exception> translate(Exception e) throws Exception {
OAuth2Exception oAuth2Exception = (OAuth2Exception) e;
return ResponseEntity
//.status(oAuth2Exception.getHttpErrorCode())
.status(200)
.body(new CustomOauthException(oAuth2Exception.getMessage()));
}
}
認(rèn)證失敗序列化類(CustomOauthExceptionSerializer)
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.Date;
import java.util.Map;
/**
*
* @ClassName: CustomOauthExceptionSerializer
* @Description:password模式錯(cuò)誤處理,自定義登錄失敗異常信息
* @Author majinzhong
* @Date 2023/7/3 16:32
* @Version 1.0
*/
public class CustomOauthExceptionSerializer extends StdSerializer<CustomOauthException> {
private static final long serialVersionUID = 1478842053473472921L;
public CustomOauthExceptionSerializer() {
super(CustomOauthException.class);
}
@Override
public void serialize(CustomOauthException value, JsonGenerator gen, SerializerProvider provider) throws IOException {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
gen.writeStartObject();
gen.writeStringField("error", String.valueOf(value.getHttpErrorCode()));
// gen.writeStringField("message", value.getMessage());
gen.writeStringField("message", "用戶名或密碼錯(cuò)誤");
gen.writeStringField("path", request.getServletPath());
gen.writeStringField("timestamp", String.valueOf(new Date().getTime()));
if (value.getAdditionalInformation()!=null) {
for (Map.Entry<String, String> entry :
value.getAdditionalInformation().entrySet()) {
String key = entry.getKey();
String add = entry.getValue();
gen.writeStringField(key, add);
}
}
gen.writeEndObject();
}
}
認(rèn)證異常返回類(CustomOauthException)
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
/**
*
* @ClassName: CustomOauthException
* @Description:password模式錯(cuò)誤處理,自定義登錄失敗異常信息
* @Author majinzhong
* @Date 2023/7/3 16:32
* @Version 1.0
*/
@JsonSerialize(using = CustomOauthExceptionSerializer.class)
public class CustomOauthException extends OAuth2Exception {
public CustomOauthException(String msg) {
super(msg);
}
}
tokan校驗(yàn)失敗返回信息(AuthExceptionEntryPoint)同時(shí)解決瀏覽器打開(kāi)出現(xiàn)彈框的問(wèn)題
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* 自定義AuthExceptionEntryPoint用于tokan校驗(yàn)失敗返回信息
* @Author majinzhong
* @Date 2023/7/3 16:31
* @Version 1.0
*/
public class AuthExceptionEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws ServletException {
Map<String, Object> map = new HashMap<>();
//401 未授權(quán)
map.put("error", HttpServletResponse.SC_UNAUTHORIZED);
map.put("message", authException.getMessage());
map.put("path", request.getServletPath());
map.put("timestamp", String.valueOf(new Date().getTime()));
response.setContentType("application/json");
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
try {
ObjectMapper mapper = new ObjectMapper();
mapper.writeValue(response.getOutputStream(), map);
} catch (Exception e) {
throw new ServletException();
}
}
}
此時(shí),springboot集成oauth就完成了,進(jìn)行測(cè)試(post和get方式都行)
獲取token接口
127.0.0.1:8088/oauth/token?username=admin&password=123456&grant_type=password&client_id=client&client_secret=123456&scope=all
刷新token接口
127.0.0.1:8088/oauth/token?grant_type=refresh_token&client_id=client&client_secret=123456&refresh_token=2be033f2-6dab-46fb-bfc8-3f97b6a46dd4
get請(qǐng)求示例:
方式一
?方式二
?
?post請(qǐng)求和get請(qǐng)求的方式二一致
文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-695801.html
文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-695801.html
到了這里,關(guān)于SpringBoot集成Oauth2.0(密碼模式)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!