基于Spring Security的Oauth2授權(quán)實(shí)現(xiàn)方法
前言
經(jīng)過(guò)一段時(shí)間的學(xué)習(xí)Oauth2,在網(wǎng)上也借鑒學(xué)習(xí)了一些大牛的經(jīng)驗(yàn),推薦在學(xué)習(xí)的過(guò)程中多看幾遍阮一峰的《理解OAuth 2.0》,經(jīng)過(guò)對(duì)Oauth2的多種方式的實(shí)現(xiàn),個(gè)人推薦Spring Security和Oauth2的實(shí)現(xiàn)是相對(duì)優(yōu)雅的,理由如下:
1、相對(duì)于直接實(shí)現(xiàn)Oauth2,減少了很多代碼量,也就減少的查找問(wèn)題的成本。
2、通過(guò)調(diào)整配置文件,靈活配置Oauth相關(guān)配置。
3、通過(guò)結(jié)合路由組件(如zuul),更好的實(shí)現(xiàn)微服務(wù)權(quán)限控制擴(kuò)展。
Oauth2概述
oauth2根據(jù)使用場(chǎng)景不同,分成了4種模式
- 授權(quán)碼模式(authorization code)
- 簡(jiǎn)化模式(implicit)
- 密碼模式(resource owner password credentials)
- 客戶(hù)端模式(client credentials)
在項(xiàng)目中我們通常使用授權(quán)碼模式,也是四種模式中最復(fù)雜的,通常網(wǎng)站中經(jīng)常出現(xiàn)的微博,qq第三方登錄,都會(huì)采用這個(gè)形式。
Oauth2授權(quán)主要由兩部分組成:
- Authorization server:認(rèn)證服務(wù)
- Resource server:資源服務(wù)
在實(shí)際項(xiàng)目中以上兩個(gè)服務(wù)可以在一個(gè)服務(wù)器上,也可以分開(kāi)部署。
準(zhǔn)備階段
核心maven依賴(lài)如下
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-joda</artifactId>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity4</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<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>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
token的存儲(chǔ)主流有三種方式,分別為內(nèi)存、redis和數(shù)據(jù)庫(kù),在實(shí)際項(xiàng)目中通常使用redis和數(shù)據(jù)庫(kù)存儲(chǔ)。個(gè)人推薦使用mysql數(shù)據(jù)庫(kù)存儲(chǔ)。
初始化數(shù)據(jù)結(jié)構(gòu)、索引和數(shù)據(jù)SQL語(yǔ)句如下:
--
-- Oauth sql -- MYSQL
--
Drop table if exists oauth_client_details;
create table oauth_client_details (
client_id VARCHAR(255) PRIMARY KEY,
resource_ids VARCHAR(255),
client_secret VARCHAR(255),
scope VARCHAR(255),
authorized_grant_types VARCHAR(255),
web_server_redirect_uri VARCHAR(255),
authorities VARCHAR(255),
access_token_validity INTEGER,
refresh_token_validity INTEGER,
additional_information TEXT,
autoapprove VARCHAR (255) default 'false'
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Drop table if exists oauth_access_token;
create table oauth_access_token (
token_id VARCHAR(255),
token BLOB,
authentication_id VARCHAR(255),
user_name VARCHAR(255),
client_id VARCHAR(255),
authentication BLOB,
refresh_token VARCHAR(255)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Drop table if exists oauth_refresh_token;
create table oauth_refresh_token (
token_id VARCHAR(255),
token BLOB,
authentication BLOB
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Drop table if exists oauth_code;
create table oauth_code (
code VARCHAR(255),
authentication BLOB
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- Add indexes
create index token_id_index on oauth_access_token (token_id);
create index authentication_id_index on oauth_access_token (authentication_id);
create index user_name_index on oauth_access_token (user_name);
create index client_id_index on oauth_access_token (client_id);
create index refresh_token_index on oauth_access_token (refresh_token);
create index token_id_index on oauth_refresh_token (token_id);
create index code_index on oauth_code (code);
-- INSERT DEFAULT DATA
INSERT INTO `oauth_client_details` VALUES ('dev', '', 'dev', 'app', 'authorization_code', 'http://localhost:7777/', '', '3600', '3600', '{"country":"CN","country_code":"086"}', 'TAIJI');
核心配置
核心配置主要分為授權(quán)應(yīng)用和客戶(hù)端應(yīng)用兩部分,如下:
- 授權(quán)應(yīng)用:即Oauth2授權(quán)服務(wù),主要包括Spring Security、認(rèn)證服務(wù)和資源服務(wù)兩部分配置
- 客戶(hù)端應(yīng)用:即通過(guò)授權(quán)應(yīng)用進(jìn)行認(rèn)證的應(yīng)用,多個(gè)客戶(hù)端應(yīng)用間支持單點(diǎn)登錄
授權(quán)應(yīng)用主要配置如下:
application.properties鏈接已初始化Oauth2的數(shù)據(jù)庫(kù)即可
Application啟動(dòng)類(lèi),授權(quán)服務(wù)開(kāi)啟配置和Spring Security配置,如下:
@SpringBootApplication
@AutoConfigureAfter(JacksonAutoConfiguration.class)
@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
@EnableAuthorizationServer
public class Application extends WebSecurityConfigurerAdapter {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
// 啟動(dòng)的時(shí)候要注意,由于我們?cè)赾ontroller中注入了RestTemplate,所以啟動(dòng)的時(shí)候需要實(shí)例化該類(lèi)的一個(gè)實(shí)例
@Autowired
private RestTemplateBuilder builder;
// 使用RestTemplateBuilder來(lái)實(shí)例化RestTemplate對(duì)象,spring默認(rèn)已經(jīng)注入了RestTemplateBuilder實(shí)例
@Bean
public RestTemplate restTemplate() {
return builder.build();
}
@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/login").setViewName("login");
}
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.headers().frameOptions().disable();
http.authorizeRequests()
.antMatchers("/403").permitAll() // for test
.antMatchers("/login", "/oauth/authorize", "/oauth/confirm_access", "/appManager").permitAll() // for login
.antMatchers("/image", "/js/**", "/fonts/**").permitAll() // for login
.antMatchers("/j_spring_security_check").permitAll()
.antMatchers("/oauth/authorize").authenticated();
/*.anyRequest().fullyAuthenticated();*/
http.formLogin().loginPage("/login").failureUrl("/login?error").permitAll()
.and()
.authorizeRequests().anyRequest().authenticated()
.and().logout().invalidateHttpSession(true)
.and().sessionManagement().maximumSessions(1).expiredUrl("/login?expired").sessionRegistry(sessionRegistry());
http.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
http.rememberMe().disable();
http.httpBasic();
}
}
資源服務(wù)開(kāi)啟,如下:
@Configuration
@EnableResourceServer
protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http.antMatcher("/me").authorizeRequests().anyRequest().authenticated();
}
}
OAuth2認(rèn)證授權(quán)服務(wù)配置,如下:
@Configuration
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
public static final Logger logger = LoggerFactory.getLogger(AuthorizationServerConfiguration.class);
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private DataSource dataSource;
@Bean
public TokenStore tokenStore() {
return new JdbcTokenStore(dataSource);
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager);
endpoints.tokenStore(tokenStore());
// 配置TokenServices參數(shù)
DefaultTokenServices tokenServices = new DefaultTokenServices();
tokenServices.setTokenStore(endpoints.getTokenStore());
tokenServices.setSupportRefreshToken(false);
tokenServices.setClientDetailsService(endpoints.getClientDetailsService());
tokenServices.setTokenEnhancer(endpoints.getTokenEnhancer());
tokenServices.setAccessTokenValiditySeconds( (int) TimeUnit.MINUTES.toSeconds(10)); //分鐘
endpoints.tokenServices(tokenServices);
}
@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer.checkTokenAccess("isAuthenticated()");
oauthServer.allowFormAuthenticationForClients();
}
@Bean
public ClientDetailsService clientDetails() {
return new JdbcClientDetailsService(dataSource);
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.withClientDetails(clientDetails());
/*
* 基于內(nèi)存配置項(xiàng)
* clients.inMemory()
.withClient("community")
.secret("community")
.authorizedGrantTypes("authorization_code").redirectUris("http://tech.taiji.com.cn/")
.scopes("app").and() .withClient("dev")
.secret("dev")
.authorizedGrantTypes("authorization_code").redirectUris("http://localhost:7777/")
.scopes("app");*/
}
}
客戶(hù)端應(yīng)用主要配置如下:
application.properties中Oauth2配置,如下
security.oauth2.client.clientId=dev security.oauth2.client.clientSecret=dev security.oauth2.client.accessTokenUri=http://localhost:9999/oauth/token security.oauth2.client.userAuthorizationUri=http://localhost:9999/oauth/authorize security.oauth2.resource.loadBalanced=true security.oauth2.resource.userInfoUri=http://localhost:9999/me security.oauth2.resource.logout.url=http://localhost:9999/revoke-token security.oauth2.default.roleName=ROLE_USER
Oauth2Config配置,授權(quán)Oauth2Sso配置和Spring Security配置,如下:
@Configuration
@EnableOAuth2Sso
public class Oauth2Config extends WebSecurityConfigurerAdapter{
@Autowired
CustomSsoLogoutHandler customSsoLogoutHandler;
@Autowired
OAuth2ClientContext oauth2ClientContext;
@Bean
public HttpFirewall allowUrlEncodedSlashHttpFirewall() {
StrictHttpFirewall firewall = new StrictHttpFirewall();
firewall.setAllowUrlEncodedSlash(true);
firewall.setAllowSemicolon(true);
return firewall;
}
@Bean
@ConfigurationProperties("security.oauth2.client")
public AuthorizationCodeResourceDetails taiji() {
return new AuthorizationCodeResourceDetails();
}
@Bean
public CommunitySuccessHandler customSuccessHandler() {
CommunitySuccessHandler customSuccessHandler = new CommunitySuccessHandler();
customSuccessHandler.setDefaultTargetUrl("/");
return customSuccessHandler;
}
@Bean
public CustomFailureHandler customFailureHandler() {
CustomFailureHandler customFailureHandler = new CustomFailureHandler();
customFailureHandler.setDefaultFailureUrl("/index");
return customFailureHandler;
}
@Bean
@Primary
@ConfigurationProperties("security.oauth2.resource")
public ResourceServerProperties taijiOauthorResource() {
return new ResourceServerProperties();
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
List<AuthenticationProvider> authenticationProviderList = new ArrayList<AuthenticationProvider>();
authenticationProviderList.add(customAuthenticationProvider());
AuthenticationManager authenticationManager = new ProviderManager(authenticationProviderList);
return authenticationManager;
}
@Autowired
public TaijiUserDetailServiceImpl userDetailsService;
@Bean
public TaijiAuthenticationProvider customAuthenticationProvider() {
TaijiAuthenticationProvider customAuthenticationProvider = new TaijiAuthenticationProvider();
customAuthenticationProvider.setUserDetailsService(userDetailsService);
return customAuthenticationProvider;
}
@Autowired
private MenuService menuService;
@Autowired
private RoleService roleService;
@Bean
public TaijiSecurityMetadataSource taijiSecurityMetadataSource() {
TaijiSecurityMetadataSource fisMetadataSource = new TaijiSecurityMetadataSource();
// fisMetadataSource.setMenuService(menuService);
fisMetadataSource.setRoleService(roleService);
return fisMetadataSource;
}
@Autowired
private CommunityAccessDecisionManager accessDecisionManager;
@Bean
public CommunityFilterSecurityInterceptor communityfiltersecurityinterceptor() throws Exception {
CommunityFilterSecurityInterceptor taijifiltersecurityinterceptor = new CommunityFilterSecurityInterceptor();
taijifiltersecurityinterceptor.setFisMetadataSource(taijiSecurityMetadataSource());
taijifiltersecurityinterceptor.setAccessDecisionManager(accessDecisionManager);
taijifiltersecurityinterceptor.setAuthenticationManager(authenticationManagerBean());
return taijifiltersecurityinterceptor;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
// .antMatchers("/").permitAll()
// .antMatchers("/login").permitAll() //
// .antMatchers("/image").permitAll() //
// .antMatchers("/upload/*").permitAll() // for
// .antMatchers("/common/**").permitAll() // for
// .antMatchers("/community/**").permitAll()
// .antMatchers("/").anonymous()
.antMatchers("/personal/**").authenticated()
.antMatchers("/notify/**").authenticated()
.antMatchers("/admin/**").authenticated()
.antMatchers("/manage/**").authenticated()
.antMatchers("/**/personal/**").authenticated()
.antMatchers("/user/**").authenticated()
.anyRequest()
.permitAll()
// .authenticated()
.and()
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.addLogoutHandler(customSsoLogoutHandler)
.deleteCookies("JSESSIONID").invalidateHttpSession(true)
.and()
.csrf().disable()
//.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
//.and()
.addFilterBefore(loginFilter(), BasicAuthenticationFilter.class)
.addFilterAfter(communityfiltersecurityinterceptor(), FilterSecurityInterceptor.class);///TaijiSecurity權(quán)限控制
}
@Override
public void configure(WebSecurity web) throws Exception {
// 解決靜態(tài)資源被攔截的問(wèn)題
web.ignoring().antMatchers("/theme/**")
.antMatchers("/community/**")
.antMatchers("/common/**")
.antMatchers("/upload/*");
web.httpFirewall(allowUrlEncodedSlashHttpFirewall());
}
public OAuth2ClientAuthenticationProcessingFilter loginFilter() throws Exception {
OAuth2ClientAuthenticationProcessingFilter ff = new OAuth2ClientAuthenticationProcessingFilter("/login");
OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(taiji(),oauth2ClientContext);
ff.setRestTemplate(restTemplate);
UserInfoTokenServices tokenServices = new UserInfoTokenServices(taijiOauthorResource().getUserInfoUri(), taiji().getClientId());
tokenServices.setRestTemplate(restTemplate);
ff.setTokenServices(tokenServices);
ff.setAuthenticationSuccessHandler(customSuccessHandler());
ff.setAuthenticationFailureHandler(customFailureHandler());
return ff;
}
}
授權(quán)成功回調(diào)類(lèi),認(rèn)證成功用戶(hù)落地,如下:
public class CommunitySuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
protected final Log logger = LogFactory.getLog(this.getClass());
private RequestCache requestCache = new HttpSessionRequestCache();
@Autowired
private UserService userService;
@Autowired
private RoleService roleService;
@Inject
AuthenticationManager authenticationManager;
@Value("${security.oauth2.default.roleName}")
private String defaultRole;
@Inject
TaijiOperationLogService taijiOperationLogService;
@Inject
CommunityConfiguration communityConfiguration;
@Inject
private ObjectMapper objectMapper;
@ScoreRule(code="login_score")
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws ServletException, IOException {
// 存放authentication到SecurityContextHolder
SecurityContextHolder.getContext().setAuthentication(authentication);
HttpSession session = request.getSession(true);
// 在session中存放security context,方便同一個(gè)session中控制用戶(hù)的其他操作
session.setAttribute("SPRING_SECURITY_CONTEXT", SecurityContextHolder.getContext());
OAuth2Authentication oauth2Authentication = (OAuth2Authentication) authentication;
Object details = oauth2Authentication.getUserAuthentication().getDetails();
UserDto user = saveUser((Map) details);//用戶(hù)落地
Collection<GrantedAuthority> obtionedGrantedAuthorities = obtionGrantedAuthorities(user);
UsernamePasswordAuthenticationToken newToken = new UsernamePasswordAuthenticationToken(
new User(user.getLoginName(), "", true, true, true, true, obtionedGrantedAuthorities),
authentication.getCredentials(), obtionedGrantedAuthorities);
newToken.setDetails(details);
Object oath2details=oauth2Authentication.getDetails();
oauth2Authentication = new OAuth2Authentication(oauth2Authentication.getOAuth2Request(), newToken);
oauth2Authentication.setDetails(oath2details);
oauth2Authentication.setAuthenticated(true);
SecurityContextHolder.getContext().setAuthentication(oauth2Authentication);
LogUtil.log2database(taijiOperationLogService, request, user.getLoginName(), "user", "", "", "user_login", "登錄", "onAuthenticationSuccess","");
session.setAttribute("user", user);
Collection<GrantedAuthority> authorities = (Collection<GrantedAuthority>) authentication.getAuthorities();
SavedRequest savedRequest = requestCache.getRequest(request, response);
if (savedRequest == null) {
super.onAuthenticationSuccess(request, response, authentication);
return;
}
String targetUrlParameter = getTargetUrlParameter();
if (isAlwaysUseDefaultTargetUrl()
|| (targetUrlParameter != null && StringUtils.hasText(request.getParameter(targetUrlParameter)))) {
requestCache.removeRequest(request, response);
super.onAuthenticationSuccess(request, response, authentication);
return;
}
clearAuthenticationAttributes(request);
// Use the DefaultSavedRequest URL
String targetUrl = savedRequest.getRedirectUrl();
// logger.debug("Redirecting to DefaultSavedRequest Url: " + targetUrl);
logger.debug("Redirecting to last savedRequest Url: " + targetUrl);
getRedirectStrategy().sendRedirect(request, response, targetUrl);
// getRedirectStrategy().sendRedirect(request, response, this.getDefaultTargetUrl());
}
public void setRequestCache(RequestCache requestCache) {
this.requestCache = requestCache;
}
//用戶(hù)落地
private UserDto saveUser(Map userInfo) {
UserDto dto=null;
try {
String json = objectMapper.writeValueAsString(userInfo);
dto = objectMapper.readValue(json,UserDto.class);
} catch (JsonProcessingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
UserDto user=userService.findByLoginName(dto.getLoginName());
if(user!=null) {
return user;
}
Set<RoleDto> roles= new HashSet<RoleDto>();
RoleDto role = roleService.findByRoleName(defaultRole);
roles.add(role);
dto.setRoles(roles);
List<UserDto> list = new ArrayList<UserDto>();
list.add(dto);
dto.generateTokenForCommunity(communityConfiguration.getControllerSalt());
String id =userService.saveUserWithRole(dto,communityConfiguration.getControllerSalt());
dto.setId(id);
return dto;
}
/**
* Map轉(zhuǎn)成實(shí)體對(duì)象
*
* @param map map實(shí)體對(duì)象包含屬性
* @param clazz 實(shí)體對(duì)象類(lèi)型
* @return
*/
public static <T> T map2Object(Map<String, Object> map, Class<T> clazz) {
if (map == null) {
return null;
}
T obj = null;
try {
obj = clazz.newInstance();
Field[] fields = obj.getClass().getDeclaredFields();
for (Field field : fields) {
int mod = field.getModifiers();
if (Modifier.isStatic(mod) || Modifier.isFinal(mod)) {
continue;
}
field.setAccessible(true);
String filedTypeName = field.getType().getName();
if (filedTypeName.equalsIgnoreCase("java.util.date")) {
String datetimestamp = String.valueOf(map.get(field.getName()));
if (datetimestamp.equalsIgnoreCase("null")) {
field.set(obj, null);
} else {
field.set(obj, new Date(Long.parseLong(datetimestamp)));
}
} else {
String v = map.get(field.getName()).toString();
field.set(obj, map.get(field.getName()));
}
}
} catch (Exception e) {
e.printStackTrace();
}
return obj;
}
// 取得用戶(hù)的權(quán)限
private Collection<GrantedAuthority> obtionGrantedAuthorities(UserDto users) {
Collection<GrantedAuthority> authSet = new HashSet<GrantedAuthority>();
// 獲取用戶(hù)角色
Set<RoleDto> roles = users.getRoles();
if (null != roles && !roles.isEmpty())
for (RoleDto role : roles) {
authSet.add(new SimpleGrantedAuthority(role.getId()));
}
return authSet;
}
}
客戶(hù)端應(yīng)用,單點(diǎn)登錄方法,如下:
@RequestMapping(value = "/loadToken", method = { RequestMethod.GET })
public void loadToken(Model model,HttpServletResponse response,@RequestParam(value = "clientId", required = false) String clientId) {
String token = "";
RequestAttributes ra = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes sra = (ServletRequestAttributes) ra;
HttpServletRequest request = sra.getRequest();
HttpSession session = request.getSession();
if (session.getAttribute("SPRING_SECURITY_CONTEXT") != null) {
SecurityContext securityContext = (SecurityContext)session.getAttribute("SPRING_SECURITY_CONTEXT");
Authentication authentication = securityContext.getAuthentication();
OAuth2AuthenticationDetails OAuth2AuthenticationDetails = (OAuth2AuthenticationDetails) authentication.getDetails();
token = OAuth2AuthenticationDetails.getTokenValue();
}
try {
String url = "http://localhost:9999/rediect?clientId=dev&token="+token;
response.sendRedirect(url);
} catch (IOException e) {
e.printStackTrace();
}
}
服務(wù)端應(yīng)用,單點(diǎn)登錄方法,如下:
@RequestMapping("/rediect")
public String rediect(HttpServletResponse responsel, String clientId, String token) {
OAuth2Authentication authentication = tokenStore.readAuthentication(token);
if (authentication == null) {
throw new InvalidTokenException("Invalid access token: " + token);
}
OAuth2Request request = authentication.getOAuth2Request();
Map map = new HashMap();
map.put("code", request.getRequestParameters().get("code"));
map.put("grant_type", request.getRequestParameters().get("grant_type"));
map.put("response_type", request.getRequestParameters().get("response_type"));
//TODO 需要查詢(xún)一下要跳轉(zhuǎn)的Client_id配置的回調(diào)地址
map.put("redirect_uri", "http://127.0.0.1:8888");
map.put("client_id", clientId);
map.put("state", request.getRequestParameters().get("state"));
request = new OAuth2Request(map, clientId, request.getAuthorities(), request.isApproved(), request.getScope(),
request.getResourceIds(), map.get("redirect_uri").toString(), request.getResponseTypes(),request.getExtensions()); // 模擬用戶(hù)登錄
Authentication t = tokenStore.readAuthentication(token);
OAuth2Authentication auth = new OAuth2Authentication(request, t);
OAuth2AccessToken new_token = defaultTokenServices.createAccessToken(auth);
return "redirect:/user_info?access_token=" + new_token.getValue();
}
@RequestMapping({ "/user_info" })
public void user(String access_token,HttpServletResponse response) {
OAuth2Authentication auth=tokenStore.readAuthentication(access_token);
OAuth2Request request=auth.getOAuth2Request();
Map<String, String> map = new LinkedHashMap<>();
map.put("loginName", auth.getUserAuthentication().getName());
map.put("password", auth.getUserAuthentication().getName());
map.put("id", auth.getUserAuthentication().getName());
try {
response.sendRedirect(request.getRedirectUri()+"?name="+auth.getUserAuthentication().getName());
} catch (IOException e) {
e.printStackTrace();
}
}
個(gè)人總結(jié)
Oauth2的設(shè)計(jì)相對(duì)復(fù)雜,需要深入學(xué)習(xí)多看源碼才能了解內(nèi)部的一些規(guī)則,如數(shù)據(jù)token的存儲(chǔ)是用的實(shí)體序列化后內(nèi)容,需要反序列才能在項(xiàng)目是使用,也許是為了安全,但在學(xué)習(xí)過(guò)程需要提前掌握,還有在token的過(guò)期時(shí)間不能為0,通常來(lái)講過(guò)期時(shí)間為0代表長(zhǎng)期有效,但在Oauth2中則報(bào)錯(cuò),這些坑需要一點(diǎn)點(diǎn)探索。
通過(guò)集成Spring Security和Oauth2較大的提供的開(kāi)發(fā)的效率,也提供的代碼的靈活性和可用性。但封裝的核心類(lèi)需要大家都了解一下,通讀下代碼,以便在項(xiàng)目中可隨時(shí)獲取需要的參數(shù)。
示例代碼
以下是個(gè)人的一套代碼,供參考。
基于Spring Cloud的微服務(wù)框架集成Oauth2的代碼示例
Oauth2數(shù)據(jù)結(jié)構(gòu),如下:





以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- Springsecurity Oauth2如何設(shè)置token的過(guò)期時(shí)間
- 詳解使用Spring Security OAuth 實(shí)現(xiàn)OAuth 2.0 授權(quán)
- Spring Security OAuth2實(shí)現(xiàn)使用JWT的示例代碼
- Spring Security整合Oauth2實(shí)現(xiàn)流程詳解
- SpringSecurityOAuth2 如何自定義token信息
- SpringSecurity OAuth2單點(diǎn)登錄和登出的實(shí)現(xiàn)
- SpringBoot的Security和OAuth2的使用示例小結(jié)
- Spring Security OAuth過(guò)期的解決方法
- Spring Security OAuth2認(rèn)證授權(quán)示例詳解
- Spring Security OAuth2.0登出的實(shí)現(xiàn)
相關(guān)文章
java 正則表達(dá)式獲取兩個(gè)字符中間的字符串方法
今天小編就為大家分享一篇java 正則表達(dá)式獲取兩個(gè)字符中間的字符串方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-07-07
Java使用泛型實(shí)現(xiàn)棧結(jié)構(gòu)的示例代碼
泛型是JAVA重要的特性,使用泛型編程,可以使代碼復(fù)用率提高。本文將利用泛型實(shí)現(xiàn)簡(jiǎn)單的棧結(jié)構(gòu),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2022-08-08
java使用JSCH實(shí)現(xiàn)SFTP文件管理
這篇文章主要為大家詳細(xì)介紹了java使用JSCH實(shí)現(xiàn)SFTP文件管理,實(shí)現(xiàn)上傳、下載等功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-08-08
java獲取壓縮文件中的XML并解析保存到數(shù)據(jù)庫(kù)
這篇文章主要為大家詳細(xì)介紹了如何使用java實(shí)現(xiàn)獲取壓縮文件中的XML并解析保存到數(shù)據(jù)庫(kù),文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2025-06-06

