springboot2 security-oauth2 build authentication server, modify user password encryption, modify user authority authentication

1. Build an authentication server

Create a new springboot2 project, add the following two configuration classes to start the server

1. AuthorizationServerConfiguration

There are mainly 3 configuration methods

(1) Here to cancel the access restrictions of the two apis, and allow the client's id and password to be directly written into the form form

- The client is not a user, it refers to a third-party website. Use client to represent the client in the code

 

(2) The second method is used to specify the information of the client

 

(3) The third method is used to configure the storage location of the token

 

2. WebSecurityConfiguration

Mainly used to provide three beans

 

 

That's it, use the following two APIs to test

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

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

(Code) Lanzous Cloud: https://wws.lanzous.com/iG4xHlo426h

 

 

There are a few places that can be changed

(Database) Lanzous Cloud: https://wws.lanzous.com/ibQBPlo420b

(1) Client information is stored in the database

(2) Token is stored in redis

(3) User information is stored in the database

Enter the user name and return to the implementation class of the UserDetails interface

 

 

 

Two, modify user password encryption

(1) One way is to use a custom PasswordEncoder

(2) Another method is to override the logic of user login authentication

 Use md5 to add salt encryption here

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;
    }
}

 

 

 

Three, modify user authority authentication

Mainly to rewrite the permission judgment logic in accessDecisionManager

 

(1) MethodSecurityMetadataSource is used to read the permission name written on the method annotation

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;
            }
        }
    }
}

Permission item class required by the framework

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) The logic of writing permission judgment in 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

Guess you like

Origin blog.csdn.net/u013595395/article/details/113810337