Spring Security OAuth2 Provider 之 自定义开发
Posted panchanggui
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring Security OAuth2 Provider 之 自定义开发相关的知识,希望对你有一定的参考价值。
Spring OAuth2默认提供的功能难免无法满足需求,需要特殊定制,这里列举常见的几个需要特殊开发的地方。
相关文章:
Spring Security OAuth2 Provider 之 最小实现
Spring Security OAuth2 Provider 之 数据库存储
Spring Security OAuth2 Provider 之 第三方登录简单演示
Spring Security OAuth2 Provider 之 自定义开发
Spring Security OAuth2 Provider 之 整合JWT
(1)自定义生成授权码
默认规则是: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;
- }
- }
效果如下:
(2)自定义生成令牌
默认规则是: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("-", "");
- }
- }
效果如下:
(3)自定义授权页面
默认的定义如下:
org.springframework.security.oauth2.provider.endpoint.AuthorizationEndpoint
private String errorPage = "forward:/oauth/error";
org.springframework.security.oauth2.provider.endpoint.WhitelabelApprovalEndpoint
public ModelAndView getAccessConfirmation(Map<String, Object> model, HttpServletRequest request) throws Exception {
// ...
}
org.springframework.security.oauth2.provider.endpoint.WhitelabelErrorEndpoint
public ModelAndView handleError(HttpServletRequest request) {
// ...
}
设置新的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>
效果如下:
(4)自定义用户登录页面
这部分应该属于SpringSecurity范畴的。
创建用户表
- 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‘);
配置SpringSecurity
- @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 {
- }
用户登录处理部分
- @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
- }
完成的Client->ResoureServer->AuthServer的执行过程:
参考:
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
顶
踩
以上是关于Spring Security OAuth2 Provider 之 自定义开发的主要内容,如果未能解决你的问题,请参考以下文章
Spring-Security OAuth2 设置 - 无法找到 oauth2 命名空间处理程序
Spring Security OAuth2 v5:NoSuchBeanDefinitionException:'org.springframework.security.oauth2.jwt.Jwt
针对授权标头的Spring Security OAuth2 CORS问题
评论
配置自定义授权页
POM.xml 需要加入以下配置
application.yml 需要加入以下配置