Maison  >  Article  >  Java  >  Comment SpringBoot intègre-t-il SpringSecurityOauth2 pour implémenter les problèmes d'autorisation dynamique pour l'authentification ?

Comment SpringBoot intègre-t-il SpringSecurityOauth2 pour implémenter les problèmes d'autorisation dynamique pour l'authentification ?

王林
王林avant
2023-05-11 10:28:051480parcourir

Préparez

spring-boot : 2.1.4.RELEASE

spring-security-oauth3 : 2.3.3.RELEASE (Si vous souhaitez utiliser le code source, ne changez pas ce numéro de version à volonté, car la méthode d'écriture est différent à partir de la version 2.4)

mysql : 5.7

Affichage des effets

Seul postman est utilisé pour les tests ici. La page frontale n'est pas utilisée pour l'amarrage pour le moment. La prochaine version de l'allocation des autorisations du menu de rôle aura. un affichage de page

1. Accédez à l'interface ouverte http://localhost:7000/open/hello

Comment SpringBoot intègre-t-il SpringSecurityOauth2 pour implémenter les problèmes dautorisation dynamique pour lauthentification ?

2. Accédez à l'interface protégée http://localhost:7000/admin/user/info sans token

. Comment SpringBoot intègre-t-il SpringSecurityOauth2 pour implémenter les problèmes dautorisation dynamique pour lauthentification ?

3. Obtenez le jeton après la connexion, apportez le jeton Après l'accès, les informations de l'utilisateur actuellement connecté ont été renvoyées avec succès

Comment SpringBoot intègre-t-il SpringSecurityOauth2 pour implémenter les problèmes dautorisation dynamique pour lauthentification ?

Comment SpringBoot intègre-t-il SpringSecurityOauth2 pour implémenter les problèmes dautorisation dynamique pour lauthentification ?

implémentation

oauth3 a quatre modes au total. expliquez-les ici. Recherchez en ligne et vous trouverez la même chose

car maintenant nous envisageons uniquement d'effectuer des transactions à sens unique, donc le mode mot de passe est utilisé.

Il y aura un article sur SpringCloud+Oauth3 plus tard, l'authentification de la passerelle

Parlons de quelques points

1 Autorisations dynamiques de configuration de l'intercepteur

Comment SpringBoot intègre-t-il SpringSecurityOauth2 pour implémenter les problèmes dautorisation dynamique pour lauthentification ?

Créez une nouvelle classe MySecurityFilter, héritez de AbstractSecurityInterceptor et implémentez l'interface Filter.

Initialisation, gestionnaire de décision d'accès personnalisé

@PostConstruct
 public void init(){
        super.setAuthenticationManager(authenticationManager);
        super.setAccessDecisionManager(myAccessDecisionManager);
  }

Le filtre personnalisé appelle une source de métadonnées sécurisée

@Override
public SecurityMetadataSource obtainSecurityMetadataSource() {
    return this.mySecurityMetadataSource;
}

Tout d'abord, jetons un coup d'œil au code de base du filtre personnalisé appelant une source de métadonnées sécurisée

Le code suivant est utilisé pour obtenir les autorisations (rôles ) requis pour que la demande actuelle arrive

/**
     * 获得当前请求所需要的角色
     * @param object
     * @return
     * @throws IllegalArgumentException
     */
    @Override
    public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
        String requestUrl = ((FilterInvocation) object).getRequestUrl();

        if (IS_CHANGE_SECURITY) {
            loadResourceDefine();
        }
        if (requestUrl.indexOf("?") > -1) {
            requestUrl = requestUrl.substring(0, requestUrl.indexOf("?"));
        }
        UrlPathMatcher matcher = new UrlPathMatcher();
        List<Object> list = new ArrayList<>();  //无需权限的,直接返回
        list.add("/oauth/**");
        list.add("/open/**");
        if(matcher.pathsMatchesUrl(list,requestUrl))
            return null;

        Set<String> roleNames = new HashSet();
        for (Resc resc: resources) {
            String rescUrl = resc.getResc_url();
            if (matcher.pathMatchesUrl(rescUrl, requestUrl)) {
                if(resc.getParent_resc_id() != null && resc.getParent_resc_id().intValue() == 1){   //默认权限的则只要登录了,无需权限匹配都可访问
                    roleNames = new HashSet();
                    break;
                }
                Map map = new HashMap();
                map.put("resc_id", resc.getResc_id());
                // 获取能访问该资源的所有权限(角色)
                List<RoleRescDTO> roles = roleRescMapper.findAll(map);
                for (RoleRescDTO rr : roles)
                    roleNames.add(rr.getRole_name());
            }
        }

        Set<ConfigAttribute> configAttributes = new HashSet();
        for(String roleName:roleNames)
            configAttributes.add(new SecurityConfig(roleName));

        log.debug("【所需的权限(角色)】:" + configAttributes);

        return configAttributes;
    }

Jetons un coup d'œil au code de base du gestionnaire de décision d'accès personnalisé. Ce code est principalement utilisé pour déterminer l'utilisateur actuellement connecté (le rôle détenu par l'utilisateur actuellement connecté sera). être écrit dans le dernier élément) Avez-vous le rôle d'autorisation ?

@Override
    public void decide(Authentication authentication, Object o, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {
        if(configAttributes == null){   //属于白名单的,不需要权限
            return;
        }
        Iterator<ConfigAttribute> iterator = configAttributes.iterator();
        while (iterator.hasNext()){
            ConfigAttribute configAttribute = iterator.next();
            String needPermission = configAttribute.getAttribute();
            for (GrantedAuthority ga: authentication.getAuthorities()) {
                if(needPermission.equals(ga.getAuthority())){   //有权限,可访问
                    return;
                }
            }
        }
        throw new AccessDeniedException("没有权限访问");

    }

2. L'exception d'authentification personnalisée renvoie un résultat commun

Pourquoi est-ce nécessaire si cela n'est pas configuré, ce sera difficile pour le front-end et le back-end ? -fin pour comprendre le contenu renvoyé par l'échec d'authentification. Il ne peut pas être interprété de manière uniforme, alors sans plus tarder, jetons un coup d'œil à la situation de retour sans configuration et configuration

(1) Avant la personnalisation, lorsque vous ne portez pas de token. pour accéder à l'interface API protégée, le résultat renvoyé est comme ceci

Comment SpringBoot intègre-t-il SpringSecurityOauth2 pour implémenter les problèmes dautorisation dynamique pour lauthentification ?

(2) Stipulons qu'après le retour de l'interface ayant échoué à l'authentification, cela devient ce qui suit : est-il préférable pour nous de traiter et d'inviter l'utilisateur.

Comment SpringBoot intègre-t-il SpringSecurityOauth2 pour implémenter les problèmes dautorisation dynamique pour lauthentification ?

D'accord, voyons où aller. Configurez-le

Notre serveur de ressources OautyResourceConfig, réécrivez la partie suivante du code pour personnaliser le résultat renvoyé par l'exception d'authentification

Vous pouvez vous référer à ce https : //www.yisu.com/article/131668.htm

@Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
        resources.authenticationEntryPoint(authenticationEntryPoint)    //token失效或没携带token时
                .accessDeniedHandler(requestAccessDeniedHandler);   //权限不足时
    }

Comment SpringBoot intègre-t-il SpringSecurityOauth2 pour implémenter les problèmes dautorisation dynamique pour lauthentification ?

3. Obtenez l'utilisateur actuellement connecté

Le premier : utilisez JWT pour transporter les informations utilisateur, puis analysez-les après avoir obtenu le jeton

Aucune explication pour l'instant

La deuxième : écrire un SecurityUser pour implémenter l'interface UserDetails (ce projet est celui utilisé dans )

L'interface UserDetails d'origine n'a qu'un nom d'utilisateur et un mot de passe. Ici, nous ajoutons l'utilisateur dans notre système

.
protected User user;
    public SecurityUser(User user) {
        this.user = user;
    }

    public User getUser() {
        return user;
    }

Dans BaseController, chaque contrôleur en héritera et l'écrira dans la méthode getUser(), tant que l'utilisateur apporte un jeton pour y accéder, nous pouvons directement obtenir les informations de l'utilisateur actuellement connecté

protected User getUser() {
        try {
            SecurityUser userDetails = (SecurityUser) SecurityContextHolder.getContext().getAuthentication()
                    .getPrincipal();

            User user = userDetails.getUser();
            log.debug("【用户:】:" + user);

            return user;
        } catch (Exception e) {
        }
        return null;
    }

Donc, après que l'utilisateur se soit connecté avec succès, comment obtenir la collection de rôles de l'utilisateur, etc., ici nous devons implémenter l'interface UserDetailsService

Comment SpringBoot intègre-t-il SpringSecurityOauth2 pour implémenter les problèmes dautorisation dynamique pour lauthentification ?

@Service
public class TokenUserDetailsService implements UserDetailsService{

    @Autowired
    private LoginService loginService;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = loginService.loadUserByUsername(username);  //这个我们拎出来处理
        if(Objects.isNull(user))
            throw new UsernameNotFoundException("用户名不存在");
        return new SecurityUser(user);
    }
}

Ensuite, définissez le UserDetailsService dans notre classe de configuration de sécurité sur celui que nous avons écrit ci-dessus

Comment SpringBoot intègre-t-il SpringSecurityOauth2 pour implémenter les problèmes dautorisation dynamique pour lauthentification ?

@Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
    }

Enfin, nous il suffit d'implémenter notre méthode dans le loginService et de la juger en fonction de notre traitement commercial réel. Si l'utilisateur existe, etc.

@Override
    public User loadUserByUsername(String username){
        log.debug(username);
        Map map = new HashMap();
        map.put("username",username);
        map.put("is_deleted",-1);
        User user = userMapper.findByUsername(map);
        if(user != null){
            map = new HashMap();
            map.put("user_id",user.getUser_id());
            //查询用户的角色
            List<UserRoleDTO> userRoles = userRoleMapper.findAll(map);
            user.setRoles(listRoles(userRoles));
            //权限集合
            Collection<? extends GrantedAuthority> authorities = merge(userRoles);
            user.setAuthorities(authorities);
            return user;
        }
        return null;

    }

Le fichier de base de données est ici

Comment SpringBoot intègre-t-il SpringSecurityOauth2 pour implémenter les problèmes dautorisation dynamique pour lauthentification ?

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer