springboot2 security-oauth2 搭建认证服务器、修改用户密码加密、修改用户权限认证

一、搭建认证服务器

新建一个springboot2项目,加入以下两个配置类就可以启动服务器

1. AuthorizationServerConfiguration

主要有3个配置方法

(1)这边取消两个api的访问限制,以及允许将客户端的id、密码直接写入form表单

-- 客户端不是用户,是指第三方网站。代码中用client来代表客户端

(2)第二个方法,用来指定客户端的信息

(3)第三个方法用来配置token的存储位置

2. WebSecurityConfiguration

主要是用来提供三个bean

这样就可以了,用以下两个api来测试

(1)http://localhost:37001/oauth/token

(2)http://localhost:37001/oauth/check_token

(代码)蓝奏云:https://wws.lanzous.com/iG4xHlo426h

有几个地方可以再改

(数据库)蓝奏云:https://wws.lanzous.com/ibQBPlo420b

(1)客户端信息存储到数据库

(2)token存储到redis

(3)用户信息存储到数据库

输入用户名,返回UserDetails接口的实现类

二、修改用户密码加密

(1)一种方法是使用自定义的PasswordEncoder

(2)另一种方法是覆写用户登录认证的逻辑

 这里使用md5加盐加密

package hlmio.oauth2.conf.security.login;

import hlmio.oauth2.model.User;
import lombok.extern.java.Log;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;


/**
 * 登录认证的Provider,自定义实现{@link AuthenticationProvider} <br>
 */
@Log
@Component
public class LoginAuthenticationProvider implements AuthenticationProvider {

    @Autowired
    private UserDetailsService userDetailsServiceImpl;
    @Autowired
    private PasswordEncoder passwordEncoder;


    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {

        // http请求的账户密码
        String username = authentication.getName();
        String password = (String) authentication.getCredentials();

        // 根据用户名查询数据库
        UserDetails userDetails = userDetailsServiceImpl.loadUserByUsername(username);

        log.info(String.format("[http请求的账户密码]: %s/%s", username, password));

        if (userDetails == null) {
            throw new BadCredentialsException("用户名未找到");
        }

        String rawPassword = password + ((User)userDetails).getSalt();
        String encodedPassword = "{MD5}"+userDetails.getPassword();
        if(!passwordEncoder.matches(rawPassword,encodedPassword)){
            throw new BadCredentialsException("密码不正确");
        }

        return new UsernamePasswordAuthenticationToken(userDetails, password, userDetails.getAuthorities());
    }

    /**
     * 是否支持处理当前Authentication对象类型
     */
    @Override
    public boolean supports(Class<?> authentication) {
        return true;
    }
}

三、修改用户权限认证

主要是重写 accessDecisionManager 里面的权限判断逻辑

(1)MethodSecurityMetadataSource 用来读取方法注解上写的权限名

package hlmio.oauth2.conf.security.access;

import hlmio.oauth2.conf.security.other.NoAccess;
import hlmio.oauth2.conf.security.other.NoAuth;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.method.AbstractMethodSecurityMetadataSource;
import org.springframework.stereotype.Component;
import org.springframework.util.ClassUtils;


import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;


@Component
public class MyMethodSecurityMetadataSource extends AbstractMethodSecurityMetadataSource {

    public Collection<ConfigAttribute> getAttributes(Method method, Class<?> targetClass) {

        if (method.getDeclaringClass() == Object.class) {
            return Collections.emptyList();
        }
        if(targetClass.getName().indexOf("hlmio.oauth2.api") == -1){
            return Collections.emptyList();
        }

        System.out.println("method: '" + method.getName() + "'     class: '" + targetClass + "'");

        ArrayList<ConfigAttribute> attrs = new ArrayList(5);

        // region 权限(1):控制器名::方法名
        // 获取控制器的名称
        String controllerName_all = targetClass.getName();
        String[] controllerName_arr = controllerName_all.split("\\.");
        String controllerName = controllerName_arr[controllerName_arr.length-1];
        // 获取方法名
        String funcName = method.getName();
        // 权限字段的名称
        String permName = controllerName + "::" + funcName;
        System.out.println(permName);
        attrs.add(new MyConfigAttribute(permName));
        // endregion

        // region 权限(2):注解NoAuth
        NoAuth noAuth = (NoAuth)this.findAnnotation(method, targetClass, NoAuth.class);
        NoAccess noAccess = (NoAccess)this.findAnnotation(method, targetClass, NoAccess.class);
        if(noAuth!=null){
            attrs.add(new MyConfigAttribute("NoAuth"));
        }
        if(noAccess!=null){
            attrs.add(new MyConfigAttribute("NoAccess"));
        }
        // endregion


        attrs.trimToSize();
        return attrs;
    }

    public Collection<ConfigAttribute> getAllConfigAttributes() {
        return null;
    }

    private <A extends Annotation> A findAnnotation(Method method, Class<?> targetClass, Class<A> annotationClass) {
        Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
        A annotation = AnnotationUtils.findAnnotation(specificMethod, annotationClass);
        if (annotation != null) {
            this.logger.debug(annotation + " found on specific method: " + specificMethod);
            return annotation;
        } else {
            if (specificMethod != method) {
                annotation = AnnotationUtils.findAnnotation(method, annotationClass);
                if (annotation != null) {
                    this.logger.debug(annotation + " found on: " + method);
                    return annotation;
                }
            }

            annotation = AnnotationUtils.findAnnotation(specificMethod.getDeclaringClass(), annotationClass);
            if (annotation != null) {
                this.logger.debug(annotation + " found on: " + specificMethod.getDeclaringClass().getName());
                return annotation;
            } else {
                return null;
            }
        }
    }
}

框架要的权限项类

package hlmio.oauth2.conf.security.access;

import lombok.Setter;
import org.springframework.security.access.ConfigAttribute;

public class MyConfigAttribute implements ConfigAttribute {

    @Setter
    String attribute;

    MyConfigAttribute(){
    }

    MyConfigAttribute(String attribute){
        this.attribute = attribute;
    }

    @Override
    public String getAttribute(){
        return attribute;
    }
}

(2)AccessDecisionManager 里面写权限判定的逻辑

package hlmio.oauth2.conf.security.access;

import org.aopalliance.intercept.MethodInvocation;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component;

import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;


@Component
public class MyAccessDecisionManager implements AccessDecisionManager {

    public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {

        List<String> targetAccessList =  configAttributes.stream()
                                    .map( i -> i.getAttribute())
                                    .collect(Collectors.toList());
        // 放行无需登录和权限的注解
        if(targetAccessList.contains("NoAuth")){
            return;
        }
        // 拒绝未登录用户
        if (authentication == null || "anonymousUser".equals(authentication.getName())) {
            throw new AccessDeniedException("当前访问需要登录");
        }
        // 放行无需权限的注解
        if(targetAccessList.contains("NoAccess")){
            return;
        }

        List<String> userAccessList =  authentication.getAuthorities().stream()
                                            .map( i -> i.getAuthority())
                                            .collect(Collectors.toList());

        for (String s : userAccessList) {
            for (String s1 : targetAccessList) {
                if(s != null){
                    if(s.equals(s1)){
                        return;
                    }
                }
            }
        }


        throw new AccessDeniedException("当前访问没有权限");
    }

    public boolean supports(ConfigAttribute attribute) {
        return true;
    }

    public boolean supports(Class<?> clazz) {
        return MethodInvocation.class.isAssignableFrom(clazz);
    }
}

github:https://github.com/hl-mio/a1

猜你喜欢

转载自blog.csdn.net/u013595395/article/details/113810337
今日推荐