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位随机英数字。 

Java代码  技术图片
  1. @Configuration  
  2. @EnableAuthorizationServer  
  3. public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {  
  4.   
  5.     @Bean  
  6.     protected AuthorizationCodeServices authorizationCodeServices() {  
  7.         return new CustomJdbcAuthorizationCodeServices(dataSource());  
  8.     }  
  9.   
  10. }  
  11.   
  12. public class CustomJdbcAuthorizationCodeServices extends JdbcAuthorizationCodeServices {  
  13.   
  14.     private RandomValueStringGenerator generator = new RandomValueStringGenerator();  
  15.       
  16.     public CustomJdbcAuthorizationCodeServices(DataSource dataSource) {  
  17.         super(dataSource);  
  18.         this.generator = new RandomValueStringGenerator(32);  
  19.     }  
  20.       
  21.     public String createAuthorizationCode(OAuth2Authentication authentication) {  
  22.         String code = this.generator.generate();  
  23.         store(code, authentication);  
  24.         return code;  
  25.     }  
  26.   
  27. }  



效果如下: 
技术图片

(2)自定义生成令牌 

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

Java代码  技术图片
  1. @Configuration  
  2. @EnableAuthorizationServer  
  3. public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {  
  4.   
  5.     @Bean  
  6.     public TokenEnhancer tokenEnhancer() {  
  7.         return new CustomTokenEnhancer();  
  8.     }  
  9.   
  10. }  

 

Java代码  技术图片
  1. public class CustomTokenEnhancer implements TokenEnhancer {  
  2.   
  3.     @Override  
  4.     public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {  
  5.         if (accessToken instanceof DefaultOAuth2AccessToken) {  
  6.             DefaultOAuth2AccessToken token = ((DefaultOAuth2AccessToken) accessToken);  
  7.             token.setValue(getNewToken());  
  8.               
  9.             OAuth2RefreshToken refreshToken = token.getRefreshToken();  
  10.             if (refreshToken instanceof DefaultOAuth2RefreshToken) {  
  11.                 token.setRefreshToken(new DefaultOAuth2RefreshToken(getNewToken()));  
  12.             }  
  13.               
  14.             Map<String, Object> additionalInformation = new HashMap<String, Object>();  
  15.             additionalInformation.put("client_id", authentication.getOAuth2Request().getClientId());  
  16.             token.setAdditionalInformation(additionalInformation);  
  17.               
  18.             return token;  
  19.         }  
  20.         return accessToken;  
  21.     }  
  22.       
  23.     private String getNewToken() {  
  24.         return UUID.randomUUID().toString().replace("-", "");  
  25.     }  
  26.   
  27. }  



效果如下: 
技术图片

(3)自定义授权页面 

默认的定义如下: 
org.springframework.security.oauth2.provider.endpoint.AuthorizationEndpoint 

引用
  private String userApprovalPage = "forward:/oauth/confirm_access"; 
  private String errorPage = "forward:/oauth/error";



org.springframework.security.oauth2.provider.endpoint.WhitelabelApprovalEndpoint 

引用
  @RequestMapping({"/oauth/confirm_access"}) 
  public ModelAndView getAccessConfirmation(Map<String, Object> model, HttpServletRequest request) throws Exception { 
    // ... 
  }



org.springframework.security.oauth2.provider.endpoint.WhitelabelErrorEndpoint 

引用
  @RequestMapping({"/oauth/error"}) 
  public ModelAndView handleError(HttpServletRequest request) { 
    // ... 
  }



设置新的URL: 

Java代码  技术图片
  1. @Configuration  
  2. @EnableAuthorizationServer  
  3. public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {  
  4.     @Autowired  
  5.     private AuthorizationEndpoint authorizationEndpoint;  
  6.   
  7.     @PostConstruct  
  8.     public void init() {  
  9.         authorizationEndpoint.setUserApprovalPage("forward:/oauth/my_approval_page");  
  10.         authorizationEndpoint.setErrorPage("forward:/oauth/my_error_page");  
  11.     }  
  12. }  



页面实现: 

Java代码  技术图片
  1. @Controller  
  2. @SessionAttributes({ "authorizationRequest" })  
  3. public class OAuthController {  
  4.   
  5.     @RequestMapping({ "/oauth/my_approval_page" })  
  6.     public String getAccessConfirmation(Map<String, Object> model, HttpServletRequest request) throws Exception {  
  7.         @SuppressWarnings("unchecked")  
  8.         Map<String, String> scopes = (Map<String, String>) (model.containsKey("scopes") ? model.get("scopes") : request.getAttribute("scopes"));  
  9.         List<String> scopeList = new ArrayList<String>();  
  10.         for (String scope : scopes.keySet()) {  
  11.             scopeList.add(scope);  
  12.         }  
  13.         model.put("scopeList", scopeList);  
  14.         return "oauth_approval";  
  15.     }  
  16.   
  17.     @RequestMapping({ "/oauth/my_error_page" })  
  18.     public String handleError(Map<String, Object> model, HttpServletRequest request) {  
  19.         Object error = request.getAttribute("error");  
  20.         String errorSummary;  
  21.         if (error instanceof OAuth2Exception) {  
  22.             OAuth2Exception oauthError = (OAuth2Exception) error;  
  23.             errorSummary = htmlUtils.htmlEscape(oauthError.getSummary());  
  24.         } else {  
  25.             errorSummary = "Unknown error";  
  26.         }  
  27.         model.put("errorSummary", errorSummary);  
  28.         return "oauth_error";  
  29.     }  
  30. }  



/src/main/resources/templates/oauth_approval.html 

Html代码  技术图片
  1. <!DOCTYPE HTML>  
  2. <html xmlns="http://www.w3.org/1999/xhtml"  
  3.       xmlns:th="http://www.thymeleaf.org">  
  4. <head>  
  5.   <title>approval</title>  
  6.   <meta charset="utf-8"/>  
  7. </head>  
  8. <body>  
  9.   <div class="container">  
  10.     <div class="row">  
  11.       <div class="col-md-12 well">  
  12.         <h3>授权页</h3>  
  13.         应用名 : <span th:text="${session.authorizationRequest.clientId}">clientId</span>  
  14.   
  15.         <form id=‘confirmationForm‘ name=‘confirmationForm‘ th:action="@{/oauth/authorize}" method=‘post‘>  
  16.             <input name=‘user_oauth_approval‘ value=‘true‘ type=‘hidden‘ />  
  17.             <input th:name="${s}" value="true" type="hidden" th:each="s : ${scopeList}" />  
  18.             <input name=‘authorize‘ value=‘授权‘ type=‘submit‘ />  
  19.         </form>  
  20.       </div>  
  21.     </div>  
  22.   </div>  
  23. </body>  
  24. </html>  



/src/main/resources/templates/oauth_error.html 

Html代码  技术图片
  1. <!DOCTYPE HTML>  
  2. <html xmlns="http://www.w3.org/1999/xhtml"  
  3.       xmlns:th="http://www.thymeleaf.org">  
  4. <head>  
  5.   <title>error</title>  
  6.   <meta charset="utf-8"/>  
  7. </head>  
  8. <body>  
  9.   <div class="container">  
  10.     <div class="row">  
  11.       <div class="col-md-12 well">  
  12.         <h3>错误</h3>  
  13.         <style="color:red;">出错了!不能继续授权操作!</p>  
  14.         <th:utext="${errorSummary}">errorSummary  
  15.       </div>  
  16.     </div>  
  17.   </div>  
  18. </body>  
  19. </html>  



效果如下: 
技术图片 
技术图片

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

创建用户表 

Sql代码  技术图片
  1. CREATE TABLE account  
  2. (  
  3.   id serial NOT NULL,  
  4.   user_name character varying(50),  
  5.   email character varying(255),  
  6.   password character varying(512),  
  7.   role_string character varying(50),  
  8.   CONSTRAINT account_pkey PRIMARY KEY (id)  
  9. );  
  10.   
  11. INSERT INTO account(user_name, email, password, role_string)  
  12.     VALUES (‘user‘, ‘user@sample.com‘, ‘123‘, ‘ROLE_USER‘);  



配置SpringSecurity 

Java代码  技术图片
  1. @Configuration  
  2. @Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)  
  3. @EnableWebSecurity  
  4. static class SecurityConfig extends WebSecurityConfigurerAdapter {  
  5.     @Override  
  6.     protected void configure(AuthenticationManagerBuilder auth) throws Exception {  
  7.         auth.authenticationProvider(authenticationProvider);  
  8.     }  
  9.   
  10.     @Override  
  11.     protected void configure(HttpSecurity http) throws Exception {  
  12.       http.csrf().disable();  
  13.   
  14.       http.antMatcher("/oauth/**")  
  15.         .authorizeRequests()  
  16.           .antMatchers("/oauth/index").permitAll()  
  17.           .antMatchers("/oauth/token").permitAll()  
  18.           .antMatchers("/oauth/check_token").permitAll()  
  19.           .antMatchers("/oauth/confirm_access").permitAll()  
  20.           .antMatchers("/oauth/error").permitAll()  
  21.           .antMatchers("/oauth/my_approval_page").permitAll()  
  22.           .antMatchers("/oauth/my_error_page").permitAll()  
  23.           .anyRequest().authenticated()  
  24.         .and()  
  25.           .formLogin()  
  26.           .loginPage("/oauth/index")  
  27.           .loginProcessingUrl("/oauth/login");  
  28.     }  
  29.     @Autowired  
  30.     private CustomAuthenticationProvider authenticationProvider;  
  31. }  

 

Java代码  技术图片
  1. @Configuration  
  2. @MapperScan("com.rensanning")  
  3. @EnableTransactionManagement(proxyTargetClass = true)  
  4. static class RepositoryConfig {  
  5. }  



用户登录处理部分 

Java代码  技术图片
  1. @Component  
  2. public class CustomAuthenticationProvider implements AuthenticationProvider {  
  3.   
  4.   @Autowired  
  5.   private AccountService accountService;  
  6.   
  7.   @Override  
  8.   public Authentication authenticate(Authentication authentication) throws AuthenticationException {  
  9.   
  10.     String name = authentication.getName();  
  11.     String password = authentication.getCredentials().toString();  
  12.   
  13.     Account account = accountService.authUser(name, password);  
  14.     if (account == null) {  
  15.       throw new AuthenticationCredentialsNotFoundException("Account is not found.");  
  16.     }  
  17.   
  18.     List<GrantedAuthority> grantedAuths = AuthorityUtils.createAuthorityList(account.getRoleString());  
  19.     return new UsernamePasswordAuthenticationToken(name, password, grantedAuths);  
  20.   }  
  21.   
  22.   @Override  
  23.   public boolean supports(Class<?> authentication) {  
  24.     return authentication.equals(UsernamePasswordAuthenticationToken.class);  
  25.   }  
  26. }  

 

Java代码  技术图片
  1. @Service  
  2. public class AccountService {  
  3.   
  4.   @Autowired  
  5.   private AccountRepository accountRepository;  
  6.   
  7.   public Account authUser(String userName, String password) {  
  8.     Account u = accountRepository.findByUserName(userName);  
  9.     if (u == null) {  
  10.       return null;  
  11.     }  
  12.     if (!u.getPassword().equals(password)) {  
  13.       return null;  
  14.     }  
  15.     return u;  
  16.   }  
  17.   
  18. }  

 

Java代码  技术图片
  1. public interface AccountRepository {  
  2.   @Select("select id, user_name as userName, email, password, role_string as roleString from account where user_name=#{user_name}")  
  3.   Account findByUserName(String userName);  
  4. }  

 

Java代码  技术图片
  1. @SuppressWarnings("serial")  
  2. public class Account implements Serializable {  
  3.   private Integer id;  
  4.   private String userName;  
  5.   private String email;  
  6.   private String password;  
  7.   private String roleString;  
  8.   // ...setter/getter  
  9. }  


技术图片 

完成的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

1 
1 
分享到: 技术图片 技术图片
评论
3 楼 IT小新 2017-10-27  
求该例子得示例代码呗,谢谢了
2 楼 xu_yuan 2017-10-24  
LS少写了thymeleaf的配置,如果不加的话,是不能识别thymeleaf语法的html文件的。会导致controller虽然返回了oauth_approval,但是却报错error 找不到mapping

配置自定义授权页
POM.xml 需要加入以下配置
Xml代码  技术图片
  1. <!---模板引擎:网页路径渲染-->  
  2. <dependency>  
  3.     <groupId>org.springframework.boot</groupId>  
  4.     <artifactId>spring-boot-starter-thymeleaf</artifactId>  
  5. </dependency>  


application.yml 需要加入以下配置
Yml代码  技术图片
  1. spring:  
  2.   thymeleaf:  
  3.     prefix: classpath:/templates/ # 模板路径  
1 楼 ihanfeng 2017-08-31  
本例有没有相关代码实例,根据你上面的配置没搞成功,登录后又返回登录页面。
 
 
 
 
 
转自:https://www.iteye.com/blog/rensanning-2386553

以上是关于Spring Security OAuth2 Provider 之 自定义开发的主要内容,如果未能解决你的问题,请参考以下文章

Spring Security Oauth2

Spring-Security OAuth2 设置 - 无法找到 oauth2 命名空间处理程序

Spring Security OAuth2 v5:NoSuchBeanDefinitionException:'org.springframework.security.oauth2.jwt.Jwt

针对授权标头的Spring Security OAuth2 CORS问题

OAuth2.0学习(4-1)Spring Security OAuth2.0 - 代码分析

Spring Security---Oauth2详解