相关文章:
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 userApprovalPage = "forward:/oauth/confirm_access";
private String errorPage = "forward:/oauth/error";
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 {
// ...
}
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) {
// ...
}
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', '[email protected]', '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