html代码大全(很全的,北京网站优化策略,wordpress centos查看目录,wordpress首页出现恶意链接spring期刊状态Spring Stateless Security系列的第二部分是关于以无状态方式探索身份验证的方法。 如果您错过了有关CSRF的第一部分#xff0c;可以在这里找到。 因此#xff0c;在谈论身份验证时#xff0c;其全部内容就是让客户端以可验证的方式向服务器标识自己。 通常可以在这里找到。 因此在谈论身份验证时其全部内容就是让客户端以可验证的方式向服务器标识自己。 通常这始于服务器向客户端提供挑战例如要求填写用户名/密码的请求。 今天我想集中讨论通过这种初始手动挑战后会发生什么以及如何处理其他HTTP请求的自动重新身份验证。 常用方法 基于会话Cookie 我们可能最了解的最常见方法是使用服务器生成的JSESSIONID cookie形式的秘密令牌会话密钥。 这些天的初始设置几乎没有用也许会让您忘记您有一个选择要首先在这里进行。 即使不再使用此“会话密钥”来存储“会话中”的任何其他状态该密钥本身实际上也是状态 。 即如果没有这些密钥的共享和持久性存储则成功的身份验证将无法在服务器重新启动或请求负载平衡到另一台服务器后继续存在。 OAuth2 / API密钥 每当谈论REST API和安全性时 提到了OAuth2和其他类型的API密钥。 基本上它们涉及在HTTP授权标头中发送自定义令牌/密钥。 如果使用得当两种方法都可以避免客户端使用标头来处理Cookie。 这解决了CSRF漏洞和其他Cookie相关问题。 但是他们没有解决的一件事是服务器需要检查显示的身份验证密钥这几乎需要一些持久且可维护的共享存储来将密钥链接到用户/授权。 无状态方法 1. HTTP基础认证 处理认证的最古老最粗糙的方式。 只需让用户在每次请求时发送其用户名/密码即可。 这听起来似乎很可怕但是考虑到上述任何方法也都通过网络发送秘密密钥这实际上并不是那么安全。 主要是用户体验和灵活性这使得其他方法成为更好的选择。 2.服务器签名的令牌 以无状态方式处理请求中的状态的一个巧妙小技巧是让服务器对其“签名”。 然后可以在每个请求之间在客户端/服务器之间来回传输并确保它不会被调和。 这样任何用户标识数据都可以以纯文本形式共享并为其添加特殊的签名哈希。 考虑到已签名服务器可以简单地验证签名哈希是否仍与接收到的内容匹配而无需保持任何服务器端状态。 可以用于此目的的通用标准是JSON Web令牌 JWT该标准仍在起草中。 对于本博客文章我想摆脱困境跳过完全的合规性以及使用它附带的库的尖叫声。 从中挑选我们真正需要的东西。 省略了标头/变量哈希算法和url-safe base64编码 实作 如前所述我们将使用Spring Security和Spring Boot将自己的实现整合在一起。 没有任何库或精美的API会混淆令牌级别上真正发生的事情。 令牌在伪代码中看起来像这样 content toJSON(user_details)
token BASE64(content) . BASE64(HMAC(content)) 令牌中的点用作分隔符因此每个部分都可以分别标识和解码因为点字符不是任何base64编码字符串的一部分。 HMAC代表基于哈希的消息身份验证代码它基本上是使用预定义密钥从任何数据中生成的哈希。 在实际的Java中令牌的生成看起来很像伪代码 创建令牌 public String createTokenForUser(User user) {byte[] userBytes toJSON(user);byte[] hash createHmac(userBytes);final StringBuilder sb new StringBuilder(170);sb.append(toBase64(userBytes));sb.append(SEPARATOR);sb.append(toBase64(hash));return sb.toString();
} JSON中使用的相关User属性是idusernameexpires和role 但可以是您真正想要的任何东西。 我标记了杰克逊JSON序列化期间将忽略的User对象的“ password”属性因此它不会成为令牌的一部分 忽略密码 JsonIgnore
public String getPassword() {return password;
} 对于现实世界的场景您可能只想为此使用专用对象。 使用一些输入验证来防止/捕获由于对令牌进行调整而导致的解析错误令牌的解码会稍微复杂一些 解码令牌 public User parseUserFromToken(String token) {final String[] parts token.split(SEPARATOR_SPLITTER);if (parts.length 2 parts[0].length() 0 parts[1].length() 0) {try {final byte[] userBytes fromBase64(parts[0]);final byte[] hash fromBase64(parts[1]);boolean validHash Arrays.equals(createHmac(userBytes), hash);if (validHash) {final User user fromJSON(userBytes);if (new Date().getTime() user.getExpires()) {return user;}}} catch (IllegalArgumentException e) {//log tampering attempt here}}return null;
} 它本质上验证提供的哈希值是否与内容的新计算哈希值相同。 因为createHmac方法在内部使用未公开的秘密密钥来计算哈希所以没有客户端能够调整内容并提供与服务器生成的哈希相同的哈希。 仅在通过此测试后提供的数据才会被解释为表示User对象的JSON。 放大Hmac部分让我们看一下所涉及的Java。 首先必须使用一个私钥对其进行初始化这是TokenHandler的构造函数的一部分 HMAC初始化 ...
private static final String HMAC_ALGO HmacSHA256;private final Mac hmac;public TokenHandler(byte[] secretKey) {try {hmac Mac.getInstance(HMAC_ALGO);hmac.init(new SecretKeySpec(secretKey, HMAC_ALGO));} catch (NoSuchAlgorithmException | InvalidKeyException e) {throw new IllegalStateException(failed to initialize HMAC: e.getMessage(), e);}
}
... 初始化后可以使用一个方法调用重新使用它 doFinal的JavaDoc读取“处理给定的字节数组并完成MAC操作。对该方法的调用会将这个Mac对象重置为先前通过调用initKey或initKeyAlgorithmParameterSpec进行初始化时所处的状态。 …” createHmac // synchronized to guard internal hmac object
private synchronized byte[] createHmac(byte[] content) {return hmac.doFinal(content);
} 我在这里使用了一些粗略的同步以防止在Spring Singleton Service中使用时发生冲突。 实际的方法非常快〜0.01ms因此除非您每台服务器每秒要发送10k 请求否则它不会造成任何问题。 说到服务让我们一路攀升到完全可运行的基于令牌的身份验证服务 令牌认证服务 Service
public class TokenAuthenticationService {private static final String AUTH_HEADER_NAME X-AUTH-TOKEN;private static final long TEN_DAYS 1000 * 60 * 60 * 24 * 10;private final TokenHandler tokenHandler;Autowiredpublic TokenAuthenticationService(Value(${token.secret}) String secret) {tokenHandler new TokenHandler(DatatypeConverter.parseBase64Binary(secret));}public void addAuthentication(HttpServletResponse response, UserAuthentication authentication) {final User user authentication.getDetails();user.setExpires(System.currentTimeMillis() TEN_DAYS);response.addHeader(AUTH_HEADER_NAME, tokenHandler.createTokenForUser(user));}public Authentication getAuthentication(HttpServletRequest request) {final String token request.getHeader(AUTH_HEADER_NAME);if (token ! null) {final User user tokenHandler.parseUserFromToken(token);if (user ! null) {return new UserAuthentication(user);}}return null;}
} 很简单初始化一个私有TokenHandler来完成繁重的工作。 它提供了添加和读取自定义HTTP令牌标头的方法。 如您所见它不使用任何数据库驱动的UserDetailsService查找用户详细信息。 通过令牌提供了让Spring Security处理进一步的授权检查所需的所有详细信息。 最后我们现在可以将所有这些插件插入到Spring Security中在Security配置中添加两个自定义过滤器 StatelessAuthenticationSecurityConfig内部的安全性配置 ...
Override
protected void configure(HttpSecurity http) throws Exception {http...// custom JSON based authentication by POST of // {username:name,password:password} // which sets the token header upon authentication.addFilterBefore(new StatelessLoginFilter(/api/login, ...), UsernamePasswordAuthenticationFilter.class)// custom Token based authentication based on // the header previously given to the client.addFilterBefore(new StatelessAuthenticationFilter(...), UsernamePasswordAuthenticationFilter.class);
}
... StatelessLoginFilter在成功认证后添加令牌 StatelessLoginFilter ...
Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response,FilterChain chain, Authentication authentication) throws IOException, ServletException {// Lookup the complete User object from the database and create an Authentication for itfinal User authenticatedUser userDetailsService.loadUserByUsername(authentication.getName());final UserAuthentication userAuthentication new UserAuthentication(authenticatedUser);// Add the custom token as HTTP header to the responsetokenAuthenticationService.addAuthentication(response, userAuthentication);// Add the authentication to the Security contextSecurityContextHolder.getContext().setAuthentication(userAuthentication);
}
... StatelessAuthenticationFilter仅根据标头设置身份验证 StatelessAuthenticationFilter ...
Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {SecurityContextHolder.getContext().setAuthentication(tokenAuthenticationService.getAuthentication((HttpServletRequest) req));chain.doFilter(req, res); // always continue
}
... 请注意与大多数与Spring Security相关的过滤器不同无论身份验证是否成功我都选择继续沿过滤器链向下移动。 我想支持触发Spring的AnonymousAuthenticationFilter以支持匿名身份验证。 这里最大的区别是过滤器未配置为映射到任何专门用于身份验证的URL因此不提供标头并不是真正的问题。 客户端实施 客户端实现同样非常简单。 再次我将其保持为简约以防止在AngularJS详细信息中丢失身份验证位。 如果您正在寻找一个更完整地与路由集成的AngularJS JWT示例则应在此处查看 。 我从中借用了一些拦截器逻辑。 登录只需存储令牌在localStorage中 登录 $scope.login function () {var credentials { username: $scope.username, password: $scope.password };$http.post(/api/login, credentials).success(function (result, status, headers) {$scope.authenticated true;TokenStorage.store(headers(X-AUTH-TOKEN));});
}; 注销甚至更简单无需调用服务器 登出 $scope.logout function () {// Just clear the local storageTokenStorage.clear(); $scope.authenticated false;
}; 要检查用户是否“已经登录”ng-init “ init”可以很好地工作 在里面 $scope.init function () {$http.get(/api/users/current).success(function (user) {if(user.username ! anonymousUser){$scope.authenticated true;$scope.username user.username;}});
}; 我选择使用匿名可访问的端点来防止触发401/403。 您还可以解码令牌本身并检查到期时间并相信本地客户端时间足够准确。 最后为了使添加标头的过程自动化就像上一个博客条目中那样一个简单的拦截器做得很好 令牌验证拦截器 factory(TokenAuthInterceptor, function($q, TokenStorage) {return {request: function(config) {var authToken TokenStorage.retrieve();if (authToken) {config.headers[X-AUTH-TOKEN] authToken;}return config;},responseError: function(error) {if (error.status 401 || error.status 403) {TokenStorage.clear();}return $q.reject(error);}};
}).config(function($httpProvider) {$httpProvider.interceptors.push(TokenAuthInterceptor);
}); 如果客户端不会允许对需要更高特权的区域进行调用它还将负责在收到HTTP 401或403之后自动清除令牌。 令牌存储 TokenStorage只是对localStorage的包装服务我不会打扰您。 将令牌放到localStorage中可以防止脚本像保存cookie一样在保存脚本的脚本源之外读取脚本。 但是由于令牌不是实际的Cookie因此无法指示任何浏览器将其自动添加到请求中。 这是至关重要的因为它可以完全防止任何形式的CSRF攻击。 因此您不必实施我以前的博客中提到的任何无状态CSRF保护。 您可以在github上找到一个完整的工作示例其中包含一些不错的功能。 确保已安装gradle 2.0并使用“ gradle build”和“ gradle run”简单地运行它。 如果要像Eclipse一样在IDE中使用它请使用“ gradle eclipse”然后从IDE内导入并运行它不需要服务器。 翻译自: https://www.javacodegeeks.com/2014/10/stateless-spring-security-part-2-stateless-authentication.htmlspring期刊状态