Spring Security OAuth2 Provider 之 自定义开发

Spring OAuth2默认提供的功能难免无法满足需求,需要特殊定制,这里列举常见的几个需要特殊开发的地方。

相关文章:
[url=http://rensanning.iteye.com/blog/2384996]Spring Security OAuth2 Provider 之 最小实现[/url]
[url=http://rensanning.iteye.com/blog/2385162]Spring Security OAuth2 Provider 之 数据库存储[/url]
[url=http://rensanning.iteye.com/blog/2386309]Spring Security OAuth2 Provider 之 第三方登录简单演示[/url]
[url=http://rensanning.iteye.com/blog/2386553]Spring Security OAuth2 Provider 之 自定义开发[/url]
[url=http://rensanning.iteye.com/blog/2386766]Spring Security OAuth2 Provider 之 整合JWT[/url]

[b](1)自定义生成授权码[/b]

默认规则是:6位随机英数字。
可以通过扩展AuthorizationCodeServices来覆写已有的生成规则。通过覆写createAuthorizationCode()方法可以设置成任意的生成规则。
比如,这里实现返回32位随机英数字。
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {

@Bean
protected AuthorizationCodeServices authorizationCodeServices() {
return new CustomJdbcAuthorizationCodeServices(dataSource());
}

}

public class CustomJdbcAuthorizationCodeServices extends JdbcAuthorizationCodeServices {

private RandomValueStringGenerator generator = new RandomValueStringGenerator();

public CustomJdbcAuthorizationCodeServices(DataSource dataSource) {
super(dataSource);
this.generator = new RandomValueStringGenerator(32);
}

public String createAuthorizationCode(OAuth2Authentication authentication) {
String code = this.generator.generate();
store(code, authentication);
return code;
}

}


效果如下:
[img]http://dl2.iteye.com/upload/attachment/0126/2143/21aa5ff9-b782-386d-8643-dfff179061cd.png[/img]

[b](2)自定义生成令牌[/b]

默认规则是:UUID。
Spring OAuth2提供了一个操作Token的接口TokenEnhancer,通过实现它可以任意操作accessToken和refreshToken。比如,这里实现将Token中的横线去掉。
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {

@Bean
public TokenEnhancer tokenEnhancer() {
return new CustomTokenEnhancer();
}

}

public class CustomTokenEnhancer implements TokenEnhancer {

@Override
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
if (accessToken instanceof DefaultOAuth2AccessToken) {
DefaultOAuth2AccessToken token = ((DefaultOAuth2AccessToken) accessToken);
token.setValue(getNewToken());

OAuth2RefreshToken refreshToken = token.getRefreshToken();
if (refreshToken instanceof DefaultOAuth2RefreshToken) {
token.setRefreshToken(new DefaultOAuth2RefreshToken(getNewToken()));
}

Map<String, Object> additionalInformation = new HashMap<String, Object>();
additionalInformation.put("client_id", authentication.getOAuth2Request().getClientId());
token.setAdditionalInformation(additionalInformation);

return token;
}
return accessToken;
}

private String getNewToken() {
return UUID.randomUUID().toString().replace("-", "");
}

}


效果如下:
[img]http://dl2.iteye.com/upload/attachment/0126/2145/ff21e53b-f0c5-3c2c-ac12-72df7bc9d9f2.png[/img]

[b](3)自定义授权页面[/b]

默认的定义如下:
org.springframework.security.oauth2.provider.endpoint.AuthorizationEndpoint
[quote] private String userApprovalPage = "forward:/oauth/confirm_access";
private String errorPage = "forward:/oauth/error";[/quote]

org.springframework.security.oauth2.provider.endpoint.WhitelabelApprovalEndpoint
[quote] @RequestMapping({"/oauth/confirm_access"})
public ModelAndView getAccessConfirmation(Map<String, Object> model, HttpServletRequest request) throws Exception {
// ...
}[/quote]

org.springframework.security.oauth2.provider.endpoint.WhitelabelErrorEndpoint
[quote] @RequestMapping({"/oauth/error"})
public ModelAndView handleError(HttpServletRequest request) {
// ...
}[/quote]

设置新的URL:
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
@Autowired
private AuthorizationEndpoint authorizationEndpoint;

@PostConstruct
public void init() {
authorizationEndpoint.setUserApprovalPage("forward:/oauth/my_approval_page");
authorizationEndpoint.setErrorPage("forward:/oauth/my_error_page");
}
}


页面实现:
@Controller
@SessionAttributes({ "authorizationRequest" })
public class OAuthController {

@RequestMapping({ "/oauth/my_approval_page" })
public String getAccessConfirmation(Map<String, Object> model, HttpServletRequest request) throws Exception {
@SuppressWarnings("unchecked")
Map<String, String> scopes = (Map<String, String>) (model.containsKey("scopes") ? model.get("scopes") : request.getAttribute("scopes"));
List<String> scopeList = new ArrayList<String>();
for (String scope : scopes.keySet()) {
scopeList.add(scope);
}
model.put("scopeList", scopeList);
return "oauth_approval";
}

@RequestMapping({ "/oauth/my_error_page" })
public String handleError(Map<String, Object> model, HttpServletRequest request) {
Object error = request.getAttribute("error");
String errorSummary;
if (error instanceof OAuth2Exception) {
OAuth2Exception oauthError = (OAuth2Exception) error;
errorSummary = HtmlUtils.htmlEscape(oauthError.getSummary());
} else {
errorSummary = "Unknown error";
}
model.put("errorSummary", errorSummary);
return "oauth_error";
}
}


/src/main/resources/templates/oauth_approval.html
<!DOCTYPE HTML>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org">
<head>
<title>approval</title>
<meta charset="utf-8"/>
</head>
<body>
<div class="container">
<div class="row">
<div class="col-md-12 well">
<h3>授权页</h3>
应用名 : <span th:text="${session.authorizationRequest.clientId}">clientId</span>

<form id='confirmationForm' name='confirmationForm' th:action="@{/oauth/authorize}" method='post'>
<input name='user_oauth_approval' value='true' type='hidden' />
<input th:name="${s}" value="true" type="hidden" th:each="s : ${scopeList}" />
<input name='authorize' value='授权' type='submit' />
</form>
</div>
</div>
</div>
</body>
</html>


/src/main/resources/templates/oauth_error.html
<!DOCTYPE HTML>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org">
<head>
<title>error</title>
<meta charset="utf-8"/>
</head>
<body>
<div class="container">
<div class="row">
<div class="col-md-12 well">
<h3>错误</h3>
<p style="color:red;">出错了!不能继续授权操作!</p>
<p th:utext="${errorSummary}">errorSummary
</div>
</div>
</div>
</body>
</html>


效果如下:
[img]http://dl2.iteye.com/upload/attachment/0126/2151/a1714717-d5c2-3537-bc6f-8ddb3ee90787.png[/img]
[img]http://dl2.iteye.com/upload/attachment/0126/2149/89e4a7f9-7ca9-38cf-adee-5574b27e9c76.png[/img]

[b](4)自定义用户登录页面[/b]
这部分应该属于SpringSecurity范畴的。

[b]创建用户表[/b]
CREATE TABLE account
(
id serial NOT NULL,
user_name character varying(50),
email character varying(255),
password character varying(512),
role_string character varying(50),
CONSTRAINT account_pkey PRIMARY KEY (id)
);

INSERT INTO account(user_name, email, password, role_string)
VALUES ('user', 'user@sample.com', '123', 'ROLE_USER');


[b]配置SpringSecurity[/b]
@Configuration
@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
@EnableWebSecurity
static class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider);
}

@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();

http.antMatcher("/oauth/**")
.authorizeRequests()
.antMatchers("/oauth/index").permitAll()
.antMatchers("/oauth/token").permitAll()
.antMatchers("/oauth/check_token").permitAll()
.antMatchers("/oauth/confirm_access").permitAll()
.antMatchers("/oauth/error").permitAll()
.antMatchers("/oauth/my_approval_page").permitAll()
.antMatchers("/oauth/my_error_page").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/oauth/index")
.loginProcessingUrl("/oauth/login");
}
@Autowired
private CustomAuthenticationProvider authenticationProvider;
}

@Configuration
@MapperScan("com.rensanning")
@EnableTransactionManagement(proxyTargetClass = true)
static class RepositoryConfig {
}


[b]用户登录处理部分[/b]
@Component
public class CustomAuthenticationProvider implements AuthenticationProvider {

@Autowired
private AccountService accountService;

@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {

String name = authentication.getName();
String password = authentication.getCredentials().toString();

Account account = accountService.authUser(name, password);
if (account == null) {
throw new AuthenticationCredentialsNotFoundException("Account is not found.");
}

List<GrantedAuthority> grantedAuths = AuthorityUtils.createAuthorityList(account.getRoleString());
return new UsernamePasswordAuthenticationToken(name, password, grantedAuths);
}

@Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
}

@Service
public class AccountService {

@Autowired
private AccountRepository accountRepository;

public Account authUser(String userName, String password) {
Account u = accountRepository.findByUserName(userName);
if (u == null) {
return null;
}
if (!u.getPassword().equals(password)) {
return null;
}
return u;
}

}

public interface AccountRepository {
@Select("select id, user_name as userName, email, password, role_string as roleString from account where user_name=#{user_name}")
Account findByUserName(String userName);
}

@SuppressWarnings("serial")
public class Account implements Serializable {
private Integer id;
private String userName;
private String email;
private String password;
private String roleString;
// ...setter/getter
}

[img]http://dl2.iteye.com/upload/attachment/0126/2177/3dd3a570-5a62-37bd-a16e-63c565e936ff.png[/img]

完成的Client->ResoureServer->AuthServer的执行过程:
[img]http://dl2.iteye.com/upload/attachment/0126/2179/43f6a1b9-486b-3b4a-acd0-0cba13dd74d1.gif[/img]

参考:
https://stackoverflow.com/questions/29618658/spring-how-to-create-a-custom-access-and-refresh-oauth2-token
https://stackoverflow.com/questions/29345508/spring-oauth2-custom-oauth-approval-page-at-oauth-authorize
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值