`
yangyangmyself
  • 浏览: 233957 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

Spring Cloud之OAuth2

    博客分类:
  • Java
阅读更多
备:附件中OAuth2 授权服务器实现源码及PPT
一、Authorization code grant


 
 
The flow illustrated in Figure 1 includes the following steps:
  • (A) The client (typically, a web application) initiates the flow by directing the
  • resource owner's user agent (typically, a web browser) to the authorization
  • endpoint. The client's request includes the client identifier, requested scope,
  • local state, and a redirection URI. The authorization server directs the user
  • agent (typically, a web browser) back to the redirect URI after the access is
  • granted (or denied).
  • (B) The resource owner authenticates with the authorization server through
  • the user agent and grants or denies the client's access request.
  • (C) If the resource owner grants access, the authorization server redirects the
  • user agent (typically, a web browser) back to the client using the redirection URI
  • provided earlier (in the request or during client registration). The redirection URI
  • includes an authorization code and any local state provided by the client earlier.
  • (D) The client makes an access token request from the authorization server's token
  • endpoint by including the authorization code received in the previous step. When
  • making the request, the client authenticates with the authorization server using
  • the client credentials. The client also includes the redirection URI used to obtain
  • the authorization code for verification.
  • (E) The authorization server authenticates the client. It validates the authorization
  • code and ensures that the redirection URI received matches the URI used to redirect
  • the client in step (C). If valid, the authorization server responds back with an access
  • token and, optionally, a refresh token in case an offline access was requested.
Authorization code request
The authorization code request corresponds to steps (A) and (B) as described in Figure
1. In step (A), the client makes a request to the authorization server in the 
application/x-www-form-urlencoded format, as shown in Listing 1.
Listing 1. Example of an authorization code request
1
2
3
GET /authorize?response_type=code&client_id=s6BhdRkqt3&state=xyz
&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb HTTP/1.1
Host: server.example.com
The request must contain the following parameters:
  • response_type: Required. The value must be set to code.
  • client_id: Required. The client ID.
  • redirect_uri: Required. Used for user agent redirection.
  • scope: Optional. The scope of the access request.
  • state: Optional. To maintain the state between the request and callback.
After the request is verified by the authorization server, the server sends an HTTP
redirect code 302 response back to the client. The response will also include a redirection
URI in the http Location header. In step (B), the client must redirect the user agent
(typically, a web browser) to this URI. This redirection URI is usually a login page
where the resource owner can sign in with their credentials and grant/revoke access to the client's request.
Authorization code response
The authorization code response is shown in step (C) of Figure 1. If the resource
owner grants the access request, the authorization server issues an authorization
code. The authorization server redirects the user agent to the redirect URI provided
as a part of the request in step (A) and includes the authorization code as a part of
the query component of the redirection URI using the application/x-www-form-urlencodedformat.
The URI parameters are as follows:
  • Code: Required. The authorization code generated by the authorization server.
  • The code is temporary and must expire shortly after it was generated. The client
  • must not use the authorization code more than once. Any further requests using
  • the same code should be revoked by the authorization server. The authorization
  • code is bound to the client identifier and the redirection URI.
  • State: Required. If the state parameter was present in the client's authorization
  • code request, this parameter must be set to the exact value received from the client.
Access token request
This corresponds to step (D) in Figure 1. The client makes a request to the token
endpoint (authorization server) using the application/x-www-form-urlencoded format as shown in Listing 2.
Listing 2. Example of an access token request
1
2
3
4
5
6
7
POST /token HTTP/1.1
Host: server.example.com
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code&code=SplxlOBeZQQYbYS6WxSbIA
&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom&client_id=c342
The access token request must have the following parameters set:
  • grant_type: Required. The value must be set to authorization_code.
  • client_id: Required. The client ID.
  • client_secret: Optional. Secret. To authenticate with authorization server.
  • code: Required. The authorization code received from the server.
  • redirect_uri: Required. Identical to that sent in step (A).
The authorization server verifies that the code and redirect URI are valid. In the case
of confidential clients, the authorization server also authenticates the client using its
client credentials passed in the body of the request or in the authorization header.
Access token response
This corresponds to step (E) in Figure 1. If the access token request is valid and is
authorized, the authorization server returns the access token in an access token
response. An example of a successful response is shown in Listing 3.
Listing 3. Example of a successful access token response
1
2
3
4
5
6
7
8
9
10
11
12
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache

{
"access_token":"2YotnFZFEjr1zCsicMWpAA",
"token_type":"Bearer",
"expires_in":3600,
"refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",
"example_parameter":"example_value"
}
If the request is not valid or unauthorized, the authorization server returns an
appropriate error message with code.
Refresh access token request
This is an optional step, which is applicable if the client requested offline access
and was provided arefresh_token as a part of the access token request. An access token
is temporary and usually expires after an hour. After the access token expires, the
client would need to repeat the authentication process and the resource owner
would need to log in and provide authorization to enable the client to make the
access token request again.
If the client needs to refresh access tokens while the resource owner is not present
at the browser to log in and authenticate, the client uses the offline access. The
client can request an offline access while making the first authorization code
request (see step (A)). Under this scheme, the authorization server returns a refresh
token in addition to the access token. The refresh token is a long-living token that
does not expire, unless it is explicitly revoked by the resource owner. Every time
the access token expires, the client can use the refresh token to regenerate an
access token without the resource owner needing to sign in and authorize the access request.
The client makes a request to the token endpoint (authorization server) using
the application/x-www-form-urlencoded format, as shown in Listing 4:
Listing 4. Request to the token endpoint
1
2
3
4
5
6
POST /token HTTP/1.1
Host: server.example.com
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded

grant_type=refresh_token&refresh_token=tGzv3JOkF0XG5Qx2TlKWIA
The request parameters are defined as follows:
  • grant_type: Required. The value must be set to refresh_token.
  • refresh_token: Required. This is retrieved earlier from access token request.
  • scope: Optional. The scope of the access request.
The authorization server verifies the refresh token and issues a new access token.
Refresh access token response
If the request is successful, the authorization server returns a new access token.
An example of a successful response is shown in Listing 5.
Listing 5. Refresh access token response
1
2
3
4
5
6
7
8
9
10
11
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache

{
"access_token":"2YotnFZFEjr1zCsicMWpAA",
"token_type":"Bearer",
"expires_in":3600,
"example_parameter":"example_value"
}
If the request is not valid or unauthorized, the authorization server returns an
appropriate error message with code.
 
 
二、用户评证
 
 
 
三、SAML/JWT


JWT:
 


 

 
 
 
 
 
SAML:


 
 
SAML  2 种典型模式
在协议的角度, SAML 原理非常类似 CAS  Kerberos  CAS 协议依赖于 CAS Server  
Kerberos依赖于 KDC ,而 SAML 则依赖于 Identity Provider 
根据 Service Provider( 以下简称 SP)  Identity Provider( 以下简称 IDP) 的交互方式, 
SAML 可以分为以下几种模式:一种是 SP 拉方式,一种是 IDP 推方式。
 SAML 中,最重要的环节是 SP 如何获取对 Subject 的断言, SP 拉方式是 SP 主动到 IDP 
去了解Subject 的身份断言,而 IDP 推方式则是 IDP 主动把 Subject 的身份断言通过某种途径告诉 SP 
2.2.1 SAML  POST/Artifact Bindings 方式(即 SP 拉方式)
该方式的主要特点是, SP 获得客户端的凭证  IDP  Subject 的一种身份认可 之后,主动请求 
IDP Subject 的凭证的断言。如下图所示: Subject 是根据凭证去访问 SP 的。凭证代表了 
Subject 的身份,它类似于“来自 IDP 证明:我就是 Peter ,法国公民”。
现在,让我们看看 SP 拉方式是如何进行的:
Subject 访问 SP 的受保护资源, SP 发现 Subject 的请求中没有包含任何的授权信息,
于是它重定向用户访问 IDP.
 

      
 
协议执行:
1, Subject  IDP 请求凭证 方式是提交用户名 / 密码
2, IDP 通过验证 Subject 提供的信息,来确定是否提供凭证给 Subject
3, 假如 Subject 的验证信息正确,他将获取 IDP 的凭证以及将服务请求同时提交给 SP 
4, SP 接受到 Subject 的凭证,它是提供服务之前必须验证次凭证,于是,它产生了一个 
SAML 请求,要求 IDP 对凭证断言
5, 凭证是 IDP 产生的,它当然知道凭证的内容,于是它回应一个 SAML 断言给 SP
6, SP 信任 IDP  SAML 断言,它会根据断言结果确定是否为 Subject 提供服务。
4.2.1 SAML  Redirect/POST Bindings 方式  IDP 推方式
该方式的主要特点是, IDP 交给 Subject 的不是凭证,而是断言。
过程如下图所示:
 

 
       1  Subject 访问 SP 的授权服务, SP 重定向 Subject  IDP 获取断言。
       2  IDP 会要求 Subject 提供能够证明它自己身份的手段 (Password  X.509 证书等
       3  Subject  IDP 提供了自己的帐号密码。
       4  IDP 验证密码之后,会重订向 Subject 到原来的 SP 
       5  SP 校验 IDP 的断言 注意, IDP 会对自己的断言签名, SP 信任 IDP 的证书,因此,
通过校验签名,能够确信从 Subject 过来的断言确实来自 IDP 的断言 
       6 ,如果签名正确, SP 将向 Subject 提供该服务。
 
 
四、Spring + Auth2 + Security


 
五、Authorization Server Configuration
 
 
 
 
问题:
{"timestamp":1420442772928,"status":401,"error":"Unauthorized",
"message":"Full authentication is required to access this resource","path":"/resource"}
解决办法:
Authorization:username:password
用户名密码需要通过Request Header传递(key=Authorization,value=base64(username:password))
 
Application.yml:
server.contextPath: /auth
logging:
level:
org.springframework.security: DEBUG
server:
port: 8080
keystore:
password: mySecretKey
security:
user:
name:admin
password:admin
oauth2:
client:
clientId:acme
clientSecret:acmesecret
authorized-grant-types:authorization_code,refresh_token,password
scope:openid
 
 
六、TOKEN 存储方式
(一)InMemoryTokenStore
TOKEN存储方式为默认的配置方式,AuthorizationServerEndpointsConfigurer类如下
private TokenStore tokenStore() {
if (tokenStore == null) {
if (accessTokenConverter() instanceof JwtAccessTokenConverter) {
this.tokenStore = new JwtTokenStore((JwtAccessTokenConverter) accessTokenConverter());
}
else {
this.tokenStore = new InMemoryTokenStore();
}
}
return this.tokenStore;
}
 
TOKEN生成策略接口(AuthenticationKeyGenerator),从OAuth2Authentication对
象生成唯一TOKEN KEY,基于内存TOKEN,默认生成KEY实现类为:
DefaultAuthenticationKeyGenerator,MD5算法签名

public class DefaultAuthenticationKeyGenerator implements AuthenticationKeyGenerator {
private static final String CLIENT_ID = "client_id";
private static final String SCOPE = "scope";
private static final String USERNAME = "username";
public String extractKey(OAuth2Authentication authentication) {
Map<String, String> values = new LinkedHashMap<String, String>();
OAuth2Request authorizationRequest = authentication.getOAuth2Request();
if (!authentication.isClientOnly()) {
values.put(USERNAME, authentication.getName());
}
values.put(CLIENT_ID, authorizationRequest.getClientId());
if (authorizationRequest.getScope() != null) {
values.put(SCOPE, OAuth2Utils.formatParameterList(new TreeSet<String>
(authorizationRequest.getScope())));
}
return generateKey(values);
}
protected String generateKey(Map<String, String> values) {
MessageDigest digest;
try {
digest = MessageDigest.getInstance("MD5");
byte[] bytes = digest.digest(values.toString().getBytes("UTF-8"));
return String.format("%032x", new BigInteger(1, bytes));
} catch (NoSuchAlgorithmException nsae) {
throw new IllegalStateException("MD5 algorithm not available. Fatal (should be in the JDK).", nsae);
} catch (UnsupportedEncodingException uee) {
throw new IllegalStateException("UTF-8 encoding not available. Fatal (should be in the JDK).", uee);
}
}
}
 
可以调用以下方法重写TOKEN生成方式,限于高级用法
public void setAuthenticationKeyGenerator(AuthenticationKeyGenerator authenticationKeyGenerator) {
this.authenticationKeyGenerator = authenticationKeyGenerator;
}
 

 
 
(二)JwtTokenStore
JwtTokenStore实现需要依赖AccessTokenConverter接口,AccessTokenConverter
接口实现类有两个:
1)JWT(header、content、cryto)----JwtAccessTokenConverter接口
2)普通 ------DefaultAccessTokenConverter接口(默认实现)
参考类AuthorizationServerEndpointsConfigurer:

private AccessTokenConverter accessTokenConverter() {
if (this.accessTokenConverter == null) {
accessTokenConverter = new DefaultAccessTokenConverter();
}
return this.accessTokenConverter;
}
private TokenStore tokenStore() {
if (tokenStore == null) {
if (accessTokenConverter() instanceof JwtAccessTokenConverter) {
this.tokenStore = new JwtTokenStore((JwtAccessTokenConverter)
accessTokenConverter());
}
else {
this.tokenStore = new InMemoryTokenStore();
}
}
return this.tokenStore;
}

JWT转换方式:
 
1)MAC (默认)
采用对用户(字段username)或者客户端信息(oauthrocations 中的AUTHORITIES)进签名,需要依赖
DefaultAccessTokenConverter实现,DefaultAccessTokenConverter又需要
UserAuthenticationConverter接口实现(只有一个实现类DefaultUserAuthenticationConverter),
DefaultUserAuthenticationConverter实现需要依赖UserDetailsService接口获取用户信息
签名属性:

public class DefaultUserAuthenticationConverter implements UserAuthenticationConverter {
private Collection<? extends GrantedAuthority> defaultAuthorities;
private UserDetailsService userDetailsService;
/**
* Optional {@link UserDetailsService} to use when extracting an {@link Authentication}
from the incoming map.
*
* @param userDetailsService the userDetailsService to set
*/
public void setUserDetailsService(UserDetailsService userDetailsService) {
this.userDetailsService = userDetailsService;
}
/**
* Default value for authorities if an Authentication is being created and the input has
no data for authorities.
* Note that unless this property is set, the default Authentication created by
{@link #extractAuthentication(Map)}
* will be unauthenticated.
*
* @param defaultAuthorities the defaultAuthorities to set. Default null.
*/
public void setDefaultAuthorities(String[] defaultAuthorities) {
this.defaultAuthorities = AuthorityUtils.commaSeparatedStringToAuthorityList(StringUtils
.arrayToCommaDelimitedString(defaultAuthorities));
}
public Map<String, ?> convertUserAuthentication(Authentication authentication) {
Map<String, Object> response = new LinkedHashMap<String, Object>();
response.put(USERNAME, authentication.getName());
if (authentication.getAuthorities() != null && !authentication.getAuthorities().isEmpty()) {
response.put(AUTHORITIES, AuthorityUtils.authorityListToSet(authentication.getAuthorities()));
}
return response;
}
public Map<String, ?> convertUserAuthentication(Authentication authentication) {
Map<String, Object> response = new LinkedHashMap<String, Object>();
response.put(USERNAME, authentication.getName());
if (authentication.getAuthorities() != null && !authentication.getAuthorities().isEmpty()) {
response.put(AUTHORITIES, AuthorityUtils.authorityListToSet(authentication.getAuthorities()));
}
return response;
}

签名方法:
// Key随机生成
private String verifierKey = new RandomValueStringGenerator().generate();
private Signer signer = new MacSigner(verifierKey);
private String signingKey = verifierKey;
 

 
/**
* Get the verification key for the token signatures.
*
* @return the key used to verify tokens
*/
public Map<String, String> getKey() {
Map<String, String> result = new LinkedHashMap<String, String>();
result.put("alg", signer.algorithm());
result.put("value", verifierKey);
return result;
}
 
public class MacSigner implements SignerVerifier {
private static final String DEFAULT_ALGORITHM = "HMACSHA256";
public byte[] sign(byte[] bytes) {
try {
Mac mac = Mac.getInstance(algorithm);
mac.init(key);
return mac.doFinal(bytes);
}
catch (GeneralSecurityException e) {
throw new RuntimeException(e);
}
}

 
2)RSA
 

public void setKeyPair(KeyPair keyPair) {
PrivateKey privateKey = keyPair.getPrivate();
Assert.state(privateKey instanceof RSAPrivateKey, "KeyPair must be an RSA ");
signer = new RsaSigner((RSAPrivateKey) privateKey);
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
verifier = new RsaVerifier(publicKey);
verifierKey = "-----BEGIN PUBLIC KEY-----\n" + new String(Base64.encode(publicKey.getEncoded()))
+ "\n-----END PUBLIC KEY-----";
}

 
 
(三)JdbcTokenStore
 
 
 
(四)RedisTokenStore
 
 
AbstractEndpoint
 
1)AuthorizationEndpoint---/oauth/authorize
 
2)TokenEndpoint---/oauth/token
 
3)CheckTokenEndpoint---/oauth/check_token
 
4)WhitelabelApprovalEndpoint---/oauth/confirm_acces
 
5)TokenKeyEndpoint---/oauth/token_key
 
 
七、Spring Security
 
一、基础配置
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired MyUserDetailsService detailsService;
@Override protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests() .and().formLogin().loginPage("/login").permitAll()
.defaultSuccessUrl("/", true) .and().logout().logoutUrl("/logout")
.and().sessionManagement().maximumSessions(1).expiredUrl("/expired")
.and() .and().exceptionHandling().accessDeniedPage("/accessDenied");
}
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/js/**", "/css/**", "/images/**", "/**/favicon.ico");
}
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(detailsService).passwordEncoder(new BCryptPasswordEncoder());
}
}
1.@EnableWebSecurity: 禁用Boot的默认Security配置,配合@Configuration启用自定义配置
(需要扩展WebSecurityConfigurerAdapter)
2.@EnableGlobalMethodSecurity(prePostEnabled = true): 启用Security注解
例如最常用的@PreAuthorize
3.configure(HttpSecurity): Request层面的配置,对应XML Configuration中的<http>元素
4.configure(WebSecurity): Web层面的配置,一般用来配置无需安全检查的路径
5.configure(AuthenticationManagerBuilder): 身份验证配置,用于注入
自定义身份验证Bean密码校验规则
 
二、扩展配置
 
完成基础配置之后,下一步就是实现自己的UserDetailsService和PermissionEvaluator,
分别用于自定义Principle, Authority和Permission。
@Component
public class MyUserDetailsService implements UserDetailsService {
@Autowired private LoginService loginService;
@Autowired private RoleService roleService;
@Override
public UserDetails loadUserByUsername(String username) {
if (StringUtils.isBlank(username)) {
throw new UsernameNotFoundException("用户名为空");
}
Login login = loginService.findByUsername(username).orElseThrow(() -> new UsernameNotFoundException("用户不存在"));
Set<GrantedAuthority> authorities = new HashSet<>();
roleService.getRoles(login.getId()).forEach(r -> authorities.add(new SimpleGrantedAuthority(r.getName())));
return new org.springframework.security.core.userdetails.User( username, login.getPassword(),
true,//是否可用 true,//是否过期 true,//证书不过期为true true,//账户未锁定为true authorities);
}
}
创建GrantedAuthority对象时,一般名称加上ROLE_前缀。
@Component
public class MyPermissionEvaluator implements PermissionEvaluator {
@Autowired private LoginService loginService;
@Autowired private RoleService roleService;
@Override
public boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission) {
String username = authentication.getName();
Login login = loginService.findByUsername(username).get();
return roleService.authorized(login.getId(), targetDomainObject.toString(), permission.toString());
}
@Override
public boolean hasPermission(Authentication authentication, Serializable targetId,
String targetType, Object permission) {
// not supported return false;
}
}
1. hasPermission(Authentication, Object, Object)和hasPermission(Authentication,
Serializable, String, Object)
两个方法分别对应Spring Security中两个同名的表达式。
 
 
八、授权页修改
 
1) 修改管理端点URL
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
MyAuthorizationCodeService authorizationCodeServices = authorizationCodeServices();
authorizationCodeServices.setClientDetailsService(endpoints.getClientDetailsService());
endpoints.tokenStore(tokenStore())
.tokenEnhancer(jwtTokenEnhancer())
.authorizationCodeServices(authorizationCodeServices)
.authenticationManager(authenticationManager)
.pathMapping("/oauth/authorize", "/oauth2/authorize")
.pathMapping("/oauth/token", "/oauth2/token")
.pathMapping("/oauth/check_token", "/oauth2/check_token")
.pathMapping("/oauth/confirm_access", "/oauth2/confirm_access")
.pathMapping("/oauth/token_key", "/oauth2/token_key")
.pathMapping("/oauth/error", "/oauth2/error");
}
 
2)但是授权确认页面表单写死Action
端点类:WhitelabelApprovalEndpoint
@FrameworkEndpoint
@SessionAttributes("authorizationRequest")
public class WhitelabelApprovalEndpoint {
 
@RequestMapping("/oauth/confirm_access")
public ModelAndView getAccessConfirmation(Map<String, Object> model,
HttpServletRequest request) throws Exception {
String template = createTemplate(model, request);
if (request.getAttribute("_csrf") != null) {
model.put("_csrf", request.getAttribute("_csrf"));
}
return new ModelAndView(new SpelView(template), model);
}
 
protected String createTemplate(Map<String, Object> model, HttpServletRequest request) {
String template = TEMPLATE;
if (model.containsKey("scopes") || request.getAttribute("scopes") != null) {
template = template.replace("%scopes%", createScopes(model, request)).replace("%denial%", "");
}
else {
template = template.replace("%scopes%", "").replace("%denial%", DENIAL);
}
if (model.containsKey("_csrf") || request.getAttribute("_csrf") != null) {
template = template.replace("%csrf%", CSRF);
}
else {
template = template.replace("%csrf%", "");
}
return template;
}
 
private CharSequence createScopes(Map<String, Object> model, HttpServletRequest request) {
StringBuilder builder = new StringBuilder("<ul>");
@SuppressWarnings("unchecked")
Map<String, String> scopes = (Map<String, String>) (model.containsKey("scopes") ? model.get("scopes") : request
.getAttribute("scopes"));
for (String scope : scopes.keySet()) {
String approved = "true".equals(scopes.get(scope)) ? " checked" : "";
String denied = !"true".equals(scopes.get(scope)) ? " checked" : "";
String value = SCOPE.replace("%scope%", scope).replace("%key%", scope).replace("%approved%", approved)
.replace("%denied%", denied);
builder.append(value);
}
builder.append("</ul>");
return builder.toString();
}
 
private static String CSRF = "<input type='hidden' name='${_csrf.parameterName}' value='${_csrf.token}' />";
 
private static String DENIAL = "<form id='denialForm' name='denialForm' action='${path}
/oauth/authorize' method='post'><input name='user_oauth_approval' value='false'
type='hidden'/>%csrf%<label><input name='deny' value='Deny' type='submit'/></label></form>";
 
private static String TEMPLATE = "<html><body><h1>OAuth Approval</h1>"
+ "<p>Do you authorize '${authorizationRequest.clientId}' to access your protected
resources?</p>"
+ "<form id='confirmationForm' name='confirmationForm' action='${path}
/oauth/authorize' method='post'><input name='user_oauth_approval'
value='true' type='hidden'/>%csrf%%scopes%<label><input name='authorize'
value='Authorize' type='submit'/></label></form>"
+ "%denial%</body></html>";
 
private static String SCOPE = "<li><div class='form-group'>%scope%:
<input type='radio' name='%key%'"
+ " value='true'%approved%>Approve</input> <input type='radio'
name='%key%' value='false'%denied%>Deny</input></div></li>";
}
3)解决办法
配置自动授权,则跳过第2步,直接获取授权码
builder
.withClient("SheChe")
.secret("12345678")
.authorizedGrantTypes("client_credentials","authorization_code")
.scopes("resource-server-read", "resource-server-write")
.accessTokenValiditySeconds(3600)
.autoApprove(true);
 
 
 
九、www-authenticate认证  
 
很多验证都采用这种验证方式,尤其在嵌入式领域中。
优点:方便
缺点:这种认证方式在传输过程中采用的用户名密码加密方式为
BASE-64,其解码过程非常简单,如果被嗅探密码几乎是透明的.
 
服务器收到请求后,首先会解析发送来的数据中是否包含有:
Authorization: Basic XXXX=这种格式的数据
如果没有这样的header数据
那么服务器会发送HTTP信息头WWW-Authenticate: Basic realm=""到浏览器
要求浏览器发送合法的用户名和密码到服务端,为了进一步告知浏览器,
这个页面需要认证 我们最还还是接着发送一个401错误
Header("HTTP/1.0 401 Unauthorized");
 
用户输入用户名:admin 密码:admin后,浏览器将以下面这种格式将
数据发送给服务器端:Authorization: Basic YWRtaW46YWRtaW4=
Authorization: Basic为www-authenticate认证的标准HTTP信息头
YWRtaW46YWRtaW4=是经BASE-64加密后的用户名和密码
 
 
ajax跨域访问是一个老问题了,解决方法很多,比较常用的是JSONP方法,
JSONP方法是一种非官方方法,而且这种方法只支持GET方式,不如POST方式安全。
即使使用jQuery的jsonp方法,type设为POST,也会自动变为GET。
 
官方问题说明:
“script”: Evaluates the response as JavaScript and returns it as plain text.
Disables caching by appending a query string parameter, “_=[TIMESTAMP]“,
to the URL unless the cache option is set to true.Note: This will turn POSTs into GETs for remote-domain requests.
 
如果跨域使用POST方式,可以使用创建一个隐藏的iframe来实现,与ajax上传图片原理一样,但这样会比较麻烦。
 
因此,通过设置Access-Control-Allow-Origin来实现跨域访问比较简单。
例如:客户端的域名是www.client.com,而请求的域名是www.server.com
如果直接使用ajax访问,会有以下错误
XMLHttpRequest cannot load http://www.server.com/server.PHP. No 'Access-Control-Allow-Origin'
header is present on the requested resource.Origin 'http://www.client.com' is therefore not allowed access.
在被请求的Response header中加入
// 指定允许其他域名访问  
  1. header('Access-Control-Allow-Origin:*');  
  2. // 响应类型  
  3. header('Access-Control-Allow-Methods:POST');  
  4. // 响应头设置  
  5. header('Access-Control-Allow-Headers:x-requested-with,content-type');  
就可以实现ajax POST跨域访问了
 
Access-Control-Allow-Origin:* 表示允许任何域名跨域访问
如果需要指定某域名才允许跨域访问,只需把Access-Control-Allow-Origin:*
改为Access-Control-Allow-Origin:允许的域名
例如:header('Access-Control-Allow-Origin:http://www.client.com');
 
如果需要设置多个域名允许访问,这里需要用php处理一下
例如允许 www.client.com 与 www.client2.com 可以跨域访问
 
十一、Spring Security
 
 
authentication (who are you?) and authorization (what are you allowed to do?)
Authentication
The main strategy interface for authentication is AuthenticationManager which only has one method:
public interface AuthenticationManager { Authentication authenticate(Authentication
authentication) throws AuthenticationException; }
An AuthenticationManager can do one of 3 things in its authenticate() method:
1. return an Authentication (normally with authenticated=true) if it can verify that the
input represents a valid principal.
2. throw an AuthenticationException if it believes that the input represents an invalid principal.
3. return null if it can’t decide.
 
 
The most commonly used implementation of AuthenticationManager is 
ProviderManager, which delegates to a chain of AuthenticationProvider instances.
AnAuthenticationProvider is a bit like an AuthenticationManager but it has an
extra method to allow the caller to query if it supports a given Authentication type:
publicinterfaceAuthenticationProvider{Authentication authenticate
(Authentication authentication)throwsAuthenticationException;boolean
supports(Class<?> authentication);}
 
Sometimes an application has logical groups of protected resources (e.g. all web resources
that match a path pattern /api/**), and each group can have its own dedicated
AuthenticationManager. Often, each of those is a ProviderManager, and they
share a parent. The parent is then a kind of "global" resource, acting as a fallback for all providers.


 
 
Spring 访问决策管理
 
AccessDecisionManager
1. AffirmativeBased 一票可通过
public void decide(Authentication authentication, Object object,
Collection<ConfigAttribute> configAttributes) throws AccessDeniedException {
int deny = 0;
 
for (AccessDecisionVoter voter : getDecisionVoters()) {
int result = voter.vote(authentication, object, configAttributes);
 
if (logger.isDebugEnabled()) {
logger.debug("Voter: " + voter + ", returned: " + result);
}
 
switch (result) {
case AccessDecisionVoter.ACCESS_GRANTED:
return;
 
case AccessDecisionVoter.ACCESS_DENIED:
deny++;
 
break;
 
default:
break;
}
}
 
if (deny > 0) {
throw new AccessDeniedException(messages.getMessage(
"AbstractAccessDecisionManager.accessDenied", "Access is denied"));
}
 
// To get this far, every AccessDecisionVoter abstained
checkAllowIfAllAbstainDecisions();
}
 
2. ConsensusBased 赞成>大于反对
 
public void decide(Authentication authentication, Object object,
Collection<ConfigAttribute> configAttributes) throws AccessDeniedException {
int grant = 0;
int deny = 0;
int abstain = 0;
 
for (AccessDecisionVoter voter : getDecisionVoters()) {
int result = voter.vote(authentication, object, configAttributes);
 
if (logger.isDebugEnabled()) {
logger.debug("Voter: " + voter + ", returned: " + result);
}
 
switch (result) {
case AccessDecisionVoter.ACCESS_GRANTED:
grant++;
 
break;
 
case AccessDecisionVoter.ACCESS_DENIED:
deny++;
 
break;
 
default:
abstain++;
 
break;
}
}
 
if (grant > deny) {
return;
}
 
if (deny > grant) {
throw new AccessDeniedException(messages.getMessage(
"AbstractAccessDecisionManager.accessDenied", "Access is denied"));
}
 
if ((grant == deny) && (grant != 0)) {
if (this.allowIfEqualGrantedDeniedDecisions) {
return;
}
else {
throw new AccessDeniedException(messages.getMessage(
"AbstractAccessDecisionManager.accessDenied", "Access is denied"));
}
}
 
// To get this far, every AccessDecisionVoter abstained
checkAllowIfAllAbstainDecisions();
}
 
 
3. UnanimousBased 全票通过
public void decide(Authentication authentication, Object object,
Collection<ConfigAttribute> attributes) throws AccessDeniedException {
 
int grant = 0;
int abstain = 0;
 
List<ConfigAttribute> singleAttributeList = new ArrayList<ConfigAttribute>(1);
singleAttributeList.add(null);
 
for (ConfigAttribute attribute : attributes) {
singleAttributeList.set(0, attribute);
 
for (AccessDecisionVoter voter : getDecisionVoters()) {
int result = voter.vote(authentication, object, singleAttributeList);
 
if (logger.isDebugEnabled()) {
logger.debug("Voter: " + voter + ", returned: " + result);
}
switch (result) {
case AccessDecisionVoter.ACCESS_GRANTED:
grant++;
 
break;
 
case AccessDecisionVoter.ACCESS_DENIED:
throw new AccessDeniedException(messages.getMessage(
"AbstractAccessDecisionManager.accessDenied",
"Access is denied"));
 
default:
abstain++;
 
break;
}
}
}
// To get this far, there were no deny votes
if (grant > 0) {
return;
}
// To get this far, every AccessDecisionVoter abstained
checkAllowIfAllAbstainDecisions();
}
 
AccessDecisionVoter
1. RoleVoter
2. AuthenticatedVoter
 
 
 
十二、Spring授权码换TOKEN
 
Redirect_url 验证一致性
 
 
 
参考资料:
// OAuth2 in depth: A step-by-step introduction for enterprises
 
 
 
  • 大小: 7.7 KB
  • 大小: 23.6 KB
  • 大小: 93.7 KB
  • 大小: 24.3 KB
  • 大小: 26.5 KB
  • 大小: 132.4 KB
  • 大小: 93.3 KB
1
1
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics