如何为单页AngularJS应用程序实现基本的Spring安全性(会话管理)

我目前正在构建一个单页面AngularJS应用程序,该应用程序通过REST与后端进行通信。结构如下:

一个Spring MVC WebApp项目,其中包含所有AngularJS页面和资源以及所有REST控制器。

一个真正的后端,具有用于后端通信的服务和存储库,如果可以的话,还提供一个API。REST调用将与这些服务通信(第二个项目作为第一个项目的依赖项包含在内)。

我已经考虑了很多,但是似乎找不到任何可以帮助我的东西。基本上,我只需要此应用程序的一些安全性。我想要某种非常简单的会话管理:

  • 用户登录,会话标识已创建并存储在网站上的JS / cookie中
  • 当用户重新加载页面/稍后返回时,需要进行检查以查看会话ID是否仍然有效
  • 如果会话ID无效,则任何呼叫均不应到达控制器

这是基本会话管理的一般思想,这是在Spring MVC Web应用程序(没有JSP的,只有角度和REST控制器)中实现这一点的最简单方法。

提前致谢!

回答:

其余API有2个选项:有状态或无状态。

第一个选项:HTTP会话身份验证-“经典” Spring

Security身份验证机制。如果计划在多台服务器上扩展应用程序,则需要具有带有粘性会话的负载平衡器,以便每个用户都位于同一台服务器上(或将Spring

Session与Redis一起使用)。

第二个选择:您可以选择OAuth或基于令牌的身份验证。

OAuth2是一种无状态的安全性机制,因此,如果要跨多台机器扩展应用程序,则可能更希望使用它。Spring

Security提供了OAuth2实现。OAuth2的最大问题是需要具有多个数据库表才能存储其安全令牌。

与OAuth2一样,基于令牌的身份验证是一种无状态的安全机制,因此,如果要在多个不同的服务器上进行扩展,它是另一个不错的选择。Spring

Security默认情况下不存在此身份验证机制。与OAuth2相比,它更易于使用和实现,因为它不需要持久性机制,因此它适用于所有SQL和NoSQL选项。此解决方案使用自定义令牌,该令牌是您的用户名,令牌的到期日期,密码和密钥的MD5哈希。这样可以确保如果有人窃取了您的令牌,那么他将无法提取您的用户名和密码。

我建议您研究JHipster。它将使用REST

API(使用Spring

Boot)和前端(使用AngularJS)为您生成一个Web应用程序框架。生成应用程序框架时,它将要求您在我上面描述的3种身份验证机制之间进行选择。您可以重用JHipster在Spring

MVC应用程序中生成的代码。

这是JHipster生成的TokenProvider的示例:

public class TokenProvider {

private final String secretKey;

private final int tokenValidity;

public TokenProvider(String secretKey, int tokenValidity) {

this.secretKey = secretKey;

this.tokenValidity = tokenValidity;

}

public Token createToken(UserDetails userDetails) {

long expires = System.currentTimeMillis() + 1000L * tokenValidity;

String token = userDetails.getUsername() + ":" + expires + ":" + computeSignature(userDetails, expires);

return new Token(token, expires);

}

public String computeSignature(UserDetails userDetails, long expires) {

StringBuilder signatureBuilder = new StringBuilder();

signatureBuilder.append(userDetails.getUsername()).append(":");

signatureBuilder.append(expires).append(":");

signatureBuilder.append(userDetails.getPassword()).append(":");

signatureBuilder.append(secretKey);

MessageDigest digest;

try {

digest = MessageDigest.getInstance("MD5");

} catch (NoSuchAlgorithmException e) {

throw new IllegalStateException("No MD5 algorithm available!");

}

return new String(Hex.encode(digest.digest(signatureBuilder.toString().getBytes())));

}

public String getUserNameFromToken(String authToken) {

if (null == authToken) {

return null;

}

String[] parts = authToken.split(":");

return parts[0];

}

public boolean validateToken(String authToken, UserDetails userDetails) {

String[] parts = authToken.split(":");

long expires = Long.parseLong(parts[1]);

String signature = parts[2];

String signatureToMatch = computeSignature(userDetails, expires);

return expires >= System.currentTimeMillis() && signature.equals(signatureToMatch);

}

}

安全配置:

@Configuration

@EnableWebSecurity

public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

@Inject

private Http401UnauthorizedEntryPoint authenticationEntryPoint;

@Inject

private UserDetailsService userDetailsService;

@Inject

private TokenProvider tokenProvider;

@Bean

public PasswordEncoder passwordEncoder() {

return new BCryptPasswordEncoder();

}

@Inject

public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {

auth

.userDetailsService(userDetailsService)

.passwordEncoder(passwordEncoder());

}

@Override

public void configure(WebSecurity web) throws Exception {

web.ignoring()

.antMatchers("/scripts/**/*.{js,html}");

}

@Override

protected void configure(HttpSecurity http) throws Exception {

http

.exceptionHandling()

.authenticationEntryPoint(authenticationEntryPoint)

.and()

.csrf()

.disable()

.headers()

.frameOptions()

.disable()

.sessionManagement()

.sessionCreationPolicy(SessionCreationPolicy.STATELESS)

.and()

.authorizeRequests()

.antMatchers("/api/register").permitAll()

.antMatchers("/api/activate").permitAll()

.antMatchers("/api/authenticate").permitAll()

.antMatchers("/protected/**").authenticated()

.and()

.apply(securityConfigurerAdapter());

}

@EnableGlobalMethodSecurity(prePostEnabled = true, jsr250Enabled = true)

private static class GlobalSecurityConfiguration extends GlobalMethodSecurityConfiguration {

}

private XAuthTokenConfigurer securityConfigurerAdapter() {

return new XAuthTokenConfigurer(userDetailsService, tokenProvider);

}

/**

* This allows SpEL support in Spring Data JPA @Query definitions.

*

* See https://spring.io/blog/2014/07/15/spel-support-in-spring-data-jpa-query-definitions

*/

@Bean

EvaluationContextExtension securityExtension() {

return new EvaluationContextExtensionSupport() {

@Override

public String getExtensionId() {

return "security";

}

@Override

public SecurityExpressionRoot getRootObject() {

return new SecurityExpressionRoot(SecurityContextHolder.getContext().getAuthentication()) {};

}

};

}

}

以及相应的AngularJS配置:

'use strict';

angular.module('jhipsterApp')

.factory('AuthServerProvider', function loginService($http, localStorageService, Base64) {

return {

login: function(credentials) {

var data = "username=" + credentials.username + "&password="

+ credentials.password;

return $http.post('api/authenticate', data, {

headers: {

"Content-Type": "application/x-www-form-urlencoded",

"Accept": "application/json"

}

}).success(function (response) {

localStorageService.set('token', response);

return response;

});

},

logout: function() {

//Stateless API : No server logout

localStorageService.clearAll();

},

getToken: function () {

return localStorageService.get('token');

},

hasValidToken: function () {

var token = this.getToken();

return token && token.expires && token.expires > new Date().getTime();

}

};

});

authInterceptor:

.factory('authInterceptor', function ($rootScope, $q, $location, localStorageService) {

return {

// Add authorization token to headers

request: function (config) {

config.headers = config.headers || {};

var token = localStorageService.get('token');

if (token && token.expires && token.expires > new Date().getTime()) {

config.headers['x-auth-token'] = token.token;

}

return config;

}

};

})

将authInterceptor添加到$ httpProvider中:

.config(function ($httpProvider) {

$httpProvider.interceptors.push('authInterceptor');

})

希望这会有所帮助!

以上是 如何为单页AngularJS应用程序实现基本的Spring安全性(会话管理) 的全部内容, 来源链接: utcz.com/qa/404639.html

回到顶部