Comment puis-je tester la sécurité printanière @PreAuthorize (hasRole)?
De quoi ai-je besoin pour unité de test de la hasRole le cadre d'une exiger une autorisation préalable de l'annotation sur une méthode de contrôleur?
Mon test devrait réussir parce que l'utilisateur connecté ne dispose que d'une des deux rôles, mais au lieu de cela, il échoue avec l'affirmation suivante de l'erreur:
java.lang.AssertionError: Statut
Prévu :401
Réelle :200
J'ai la méthode suivante dans MyController:
@PreAuthorize(value = "hasRole('MY_ROLE') and hasRole('MY_SECOND_ROLE')")
@RequestMapping(value = "/myurl", method = RequestMethod.GET)
public String loadPage(Model model, Authentication authentication, HttpSession session) {
...stuff to do...
}
J'ai créé le suivant: abstract-security-test.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.2.xsd">
<security:global-method-security secured-annotations="enabled" />
<security:authentication-manager alias="authManager">
<security:authentication-provider>
<security:user-service>
<security:user name="missingsecondrole" password="user" authorities="MY_ROLE" />
</security:user-service>
</security:authentication-provider>
</security:authentication-manager>
</beans>
Et dans mon unité de test, j'ai ceci:
@ContextConfiguration("classpath:/spring/abstract-security-test.xml")
public class MyTest {
private final MyController myController = new MyController();
@Autowired
private AuthenticationManager manager;
@Test
public void testValidUserWithInvalidRoleFails() throws Exception {
MockMvc mockMvc = standaloneSetup(myController).setViewResolvers(viewResolver()).build();
Authentication auth = login("missingsecondrole", "user");
mockMvc.perform(get("/myurl")
.session(session)
.flashAttr(MODEL_ATTRIBUTE_NAME, new ModelMap())
.principal(auth)).andExpect(status().isUnauthorized());
}
protected Authentication login(String name, String password) {
Authentication auth = new UsernamePasswordAuthenticationToken(name, password);
SecurityContextHolder.getContext().setAuthentication(manager.authenticate(auth));
return auth;
}
private ViewResolver viewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setPrefix("WEB-INF/views");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
}
source d'informationauteur edwardmlyte
Vous devez vous connecter pour publier un commentaire.
Mise à JOUR
Printemps de Sécurité 4 fournit soutien complet pour l'intégration avec MockMvc. Par exemple:
Le Problème
Le problème est que la définition de la SecurityContextHolder ne fonctionne pas dans ce cas. La raison en est que la SecurityContextPersistenceFilter utilisera le SecurityContextRepository pour essayer de comprendre le SecurityContext de la HttpServletRequest (par défaut, il utilise le HttpSession). Le SecurityContext il trouve (ou ne trouve pas) va se substituer à l'SecurityContext vous avez mis sur le SecurityContextHolder.
La Solution
Pour assurer que la demande est authentifié, vous devez associer votre SecurityContext à l'aide de la SecurityContextRepository que vous êtes exploitant. La valeur par défaut est la HttpSessionSecurityContextRepository. Un exemple de méthode qui vous permettra de maquette en cours de la session par un utilisateur est ci-dessous:
Les détails de comment utiliser ce pourrait être encore un peu vague, car vous pourriez ne pas savoir comment accéder à la HttpServletRequest dans MockMvc, mais continuez à lire car il est une meilleure solution.
Le rendant plus facile
Si vous voulez faire cela et d'autres liées à la Sécurité, les interactions avec les MockMvc plus facile, vous pouvez vous référer à la gs-printemps-sécurité-3.2 exemple d'application. Dans le cadre du projet, vous trouverez quelques utilitaires pour travailler avec Ressort de Sécurité et de MockMvc appelé SecurityRequestPostProcessors. Vous pouvez copier que mentionné précédemment classe dans votre projet. L'utilisation de cet utilitaire va vous permettre d'écrire quelque chose comme ceci à la place:
NOTE: Il n'est pas nécessaire de définir le capital de la demande, comme le Printemps de Sécurité établit le Principal pour vous aussi longtemps que l'utilisateur est authentifié.
Vous pouvez trouver d'autres exemples dans SecurityTests.
Ce projet aidera également dans d'autres intégrations entre MockMvc et de la Sécurité Printemps (c'est à dire la configuration de la demande avec le jeton CSRF lors de l'exécution d'un POST).
N'est pas inclus par défaut?
Vous pourriez vous demander pourquoi ce n'est pas inclus par défaut. La réponse est que nous n'avons tout simplement pas le temps pour le 3.2 montage. Tout le code dans l'exemple fonctionne correctement, mais nous n'étions pas assez confiant sur les conventions de nommage et exactement comment il intégré à la libération. Vous pouvez suivre SEC-2015 qui est programmé pour venir avec Ressort de Sécurité 4.0.0.M1.
Mise à jour
Votre MockMvc instance doit également contenir les springSecurityFilterChain. Pour ce faire, vous pouvez utiliser les éléments suivants:
Pour la
@Autowired
de travail, vous devez vous assurer que votre configuration de sécurité qui rend le springSecurityFilterChain dans votre@ContextConfiguration
. Pour votre configuration actuelle, cela signifie "classpath:/spring/abstract-security-test.xml" devrait contenir votre<http ..>
partie de votre configuration de sécurité (et de tous les dépendants des haricots). Sinon, vous pouvez inclure un deuxième fichier(s) dans le@ContextConfiguration
qui a votre<http ..>
partie de votre configuration de sécurité (et de tous les dépendants des haricots).Juste pour ajouter à Rob solution ci-dessus, à compter du 20 décembre 2014, il y a un bug dans le
SecurityRequestPostProcessors
classe sur la branche master de Rob réponse ci-dessus, qui empêche le rôle assigné d'être rempli.Une solution rapide est de mettre en commentaire la ligne suivante de code (ligne actuellement 181) dans le
roles(String... roles)
méthode de laUserRequestPostProcessor
intérieure statique de la classe deSecurityRequestPostProcessors
://List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>(roles.length);
.Vous avez besoin pour commenter la variable locale, et non PAS la variable membre.
Alternativement, vous pouvez insérer cette ligne juste avant le retour de la méthode:
this.authorities = authorities;
P. S j'aurais ajouté présente comme un commentaire si j'en avais eu assez de réputation.