SpringBoot19 集成SpringSecurity01 -> 环境搭建、SpringSecurity验证

1 环境搭建

  1.1 创建一个SpringBoot项目

    项目脚手架 -> 点击前往

  1.2 创建一个Restful接口

    新建一个Controller类即可

package com.example.wiremock.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author 王杨帅
 * @create 2018-05-12 21:18
 * @desc
 **/
@RestController
@RequestMapping(value = "/security")
@Slf4j
public class SecurityController {

    @GetMapping(value = "/connect")
    public String connect() {
        String result = "前后台连接成功";
        log.info("===" + result);
        return result;
    }

}
SecurityController.java

  1.3 引入SpringSecurity相关jar包

        <!--security相关-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

  1.4 启动项目

    技巧01:由于我们引入了SpringSecurity相关的jar包,所以系统会默认开启SpringSecurity的相关配置

    技巧02:可以在配置文件中关掉这个配置(即:使SpringSecurity失效)

    技巧03:启动项目后会在控制台上打印出一个密码,因为默认的SpringSecurity配置会对所有的请求都进行权限验证,如果不通过就会跳转到 /login 请求,则是一个登陆页面或者一个登陆弹出窗口,默认登陆名为 user,默认登陆密码就是启动项目是控制台打印出来的字符串

      

  1.5 访问接口

    IP + 端口 + 上下文p径 + 请求路径

http://127.0.0.1:9999/dev/security/connect

    技巧01:SpringSecurity默认的配置会默认对所有的请求都进行权限验证,所以会跳转到 /login 请求路径,画面如下;输入正确的用户名和密码后跳转到之的请求所得到的响应

      

  1.6 SpringSecurity的授权流程

    所有请求url -> BasicAuthenticationFilter / UsernamePasswordAuthenticationFilter -> FilterSecurityInterceptor -> BasicAuthenticationFilter / UsernamePasswordAuthenticationFilter -> FilterSecurityInterceptor -> controller层

    所有请求都默认进入 BasicAuthenticationFilter 过滤器进行过滤,然后进入 FilterSecurityInterceptor 过滤器进行权限验证,如果在 FilterSecurityInterceptor 中权限验证就会跳转到 /login 请求进行处理,然后在进入 BasicAuthenticationFilter 或者 UsernamePasswordAuthenticationFilter 过滤器,再进入 FilterSecurityInterceptor,只有当 FilterSecurityInterceptor 过滤通过了才会跳转到之前的请求路径

    技巧01:如果在 FilterSecurityInterceptor 中抛出了异常就会跳转到 ExceptionTranslationFilter 进行相应的处理

    

2 SpringSecurity验证

  直接使用SpringSecurity默认的配置进行权限验证时只有一个用户,无法满足实际开发需求;在实际的开发中需要根据不同的用户判断其权限

  技巧01:直接继承一个UserDetailsService接口即可;该接口中有一个 loadUserByUsername 方法,该方法是通过用户名查找用户信息,然后在根据查到的用户信息来判断该用户的权限

  2.1 实现 UserDetailsService

    技巧01:实现了 UserDetailsService接口的实现类必须在类级别上添加@Bean注解,目的上让Spring容器去管理这个Bean

    技巧02:可以在实现了 UserDetailsService接口的实现类中依赖注入其他Bean(例如:依赖注入持久层Bean来实现数据库操作)

    技巧03:如果实现了 UserDetailsService 接口就必须进行 SpringSecurity 配置,因为SpringSecurity会使用一个实现了PasswordEncoder接口的实现类去比较用户录入的密码和从数据库中获取到的密码是否相等

package com.example.wiremock.service;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
//import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;

/**
 * @author 王杨帅
 * @create 2018-05-12 22:09
 * @desc
 **/
@Component
@Slf4j
public class FuryUserDetailService implements UserDetailsService {

    @Autowired
    private PasswordEncoder passwordEncoder;

    // 01 依赖注入持久层(用于查找用户信息)

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        log.info("用户名:" + username); // 技巧01: /login 请求传过来的用户名会传到这里

        // 02 根据用户名到数据库中查找数据
        String pwd = passwordEncoder.encode("123321"); // 技巧02:此处是模拟的从数据库中查询到的密码

        // 03 返回一个 User 对象(技巧01:这个User对象时实现了UserDetail接口的,这里利用的是Spring框架提供的User对象,也可以使用自定义但是实现了UserDetai接口的User对象)
        return new User(username, pwd, AuthorityUtils.commaSeparatedStringToAuthorityList("admin"));
    }
}
FuryUserDetailService

  2.2 SpringSecurity配置

    技巧01:其实就是配置一个Bean而已,只不过这个Bean的返回类型是 PasswordEncoder 类型

    技巧02:可以使用实现了 PasswordEncoder接口的实现类 BCryptPasswordEncoder 作为返回类型,也可以使用自定义并且实现了  PasswordEncoder接口的类作为返回类型

    坑01:WebSecurityConfigurerAdapter 这个抽象类可能已经过时,解决办法 -> 点击前往

package com.example.wiremock.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
public class BrowserSecurityConfig extends WebSecurityConfigurerAdapter {

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
//        return new MyPasswordEncoder();
    }

    
}
BrowserSecurityConfig.java

  2.3 自定义加密类

     就是一个实现了 PasswordEncoder接口的类而已,我们可以通过该类来实现MD5加密或者一些其他的加密方式

    2.3.2 加密类

      用于实现自己的加密算法

package com.example.wiremock.config;

import org.springframework.security.crypto.password.PasswordEncoder;

/**
 * @author 王杨帅
 * @create 2018-05-12 22:41
 * @desc
 **/
public class MyPasswordEncoder implements PasswordEncoder {
    @Override
    public String encode(CharSequence charSequence) {
        return charSequence.toString();
    }

    @Override
    public boolean matches(CharSequence charSequence, String s) {
        if (charSequence.toString().equals(s)) {
            return true;
        }
        return false;
    }
}
MyPasswordEncoder.java

    2.3.3 重新进行SrpingSecurity配置

package com.example.wiremock.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
public class BrowserSecurityConfig extends WebSecurityConfigurerAdapter {

    @Bean
    public PasswordEncoder passwordEncoder() {
//        return new BCryptPasswordEncoder();
        return new MyPasswordEncoder();
    }

    
}
BrowserSecurityConfig.java

  2.4 测试

    启动项目后进入到登录页面

      

    技巧01:随便输入一个用户名(PS:由于后台没有实现根据用户名查询用户信息的逻辑,若依随便输入一个即可),输入一个固定的密码(PS:这个密码要和loadUserByUsername方法中返回的User对象中的password参数加密前的内容一致)

  2.5 进阶

    loadUserByUsername 方法的返回类型是一个User对象,这个User对象有两个默认的构造器,一个仅仅包含用户名、用户秘密和权限,另一个除了包含这些信息还包含一些用户的有效性信息

    技巧01:直接看 UserDetails 就知道了

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.springframework.security.core.userdetails;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.function.Function;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.security.core.CredentialsContainer;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.util.Assert;

public class User implements UserDetails, CredentialsContainer {
    private static final long serialVersionUID = 500L;
    private static final Log logger = LogFactory.getLog(User.class);
    private String password;
    private final String username;
    private final Set<GrantedAuthority> authorities;
    private final boolean accountNonExpired;
    private final boolean accountNonLocked;
    private final boolean credentialsNonExpired;
    private final boolean enabled;

    public User(String username, String password, Collection<? extends GrantedAuthority> authorities) {
        this(username, password, true, true, true, true, authorities);
    }

    public User(String username, String password, boolean enabled, boolean accountNonExpired, boolean credentialsNonExpired, boolean accountNonLocked, Collection<? extends GrantedAuthority> authorities) {
        if (username != null && !"".equals(username) && password != null) {
            this.username = username;
            this.password = password;
            this.enabled = enabled;
            this.accountNonExpired = accountNonExpired;
            this.credentialsNonExpired = credentialsNonExpired;
            this.accountNonLocked = accountNonLocked;
            this.authorities = Collections.unmodifiableSet(sortAuthorities(authorities));
        } else {
            throw new IllegalArgumentException("Cannot pass null or empty values to constructor");
        }
    }

    public Collection<GrantedAuthority> getAuthorities() {
        return this.authorities;
    }

    public String getPassword() {
        return this.password;
    }

    public String getUsername() {
        return this.username;
    }

    public boolean isEnabled() {
        return this.enabled;
    }

    public boolean isAccountNonExpired() {
        return this.accountNonExpired;
    }

    public boolean isAccountNonLocked() {
        return this.accountNonLocked;
    }

    public boolean isCredentialsNonExpired() {
        return this.credentialsNonExpired;
    }

    public void eraseCredentials() {
        this.password = null;
    }

    private static SortedSet<GrantedAuthority> sortAuthorities(Collection<? extends GrantedAuthority> authorities) {
        Assert.notNull(authorities, "Cannot pass a null GrantedAuthority collection");
        SortedSet<GrantedAuthority> sortedAuthorities = new TreeSet(new User.AuthorityComparator());
        Iterator var2 = authorities.iterator();

        while(var2.hasNext()) {
            GrantedAuthority grantedAuthority = (GrantedAuthority)var2.next();
            Assert.notNull(grantedAuthority, "GrantedAuthority list cannot contain any null elements");
            sortedAuthorities.add(grantedAuthority);
        }

        return sortedAuthorities;
    }

    public boolean equals(Object rhs) {
        return rhs instanceof User ? this.username.equals(((User)rhs).username) : false;
    }

    public int hashCode() {
        return this.username.hashCode();
    }

    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append(super.toString()).append(": ");
        sb.append("Username: ").append(this.username).append("; ");
        sb.append("Password: [PROTECTED]; ");
        sb.append("Enabled: ").append(this.enabled).append("; ");
        sb.append("AccountNonExpired: ").append(this.accountNonExpired).append("; ");
        sb.append("credentialsNonExpired: ").append(this.credentialsNonExpired).append("; ");
        sb.append("AccountNonLocked: ").append(this.accountNonLocked).append("; ");
        if (!this.authorities.isEmpty()) {
            sb.append("Granted Authorities: ");
            boolean first = true;
            Iterator var3 = this.authorities.iterator();

            while(var3.hasNext()) {
                GrantedAuthority auth = (GrantedAuthority)var3.next();
                if (!first) {
                    sb.append(",");
                }

                first = false;
                sb.append(auth);
            }
        } else {
            sb.append("Not granted any authorities");
        }

        return sb.toString();
    }

    public static User.UserBuilder withUsername(String username) {
        return builder().username(username);
    }

    public static User.UserBuilder builder() {
        return new User.UserBuilder();
    }

    /** @deprecated */
    @Deprecated
    public static User.UserBuilder withDefaultPasswordEncoder() {
        logger.warn("User.withDefaultPasswordEncoder() is considered unsafe for production and is only intended for sample applications.");
        PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
        User.UserBuilder var10000 = builder();
        encoder.getClass();
        return var10000.passwordEncoder(encoder::encode);
    }

    public static User.UserBuilder withUserDetails(UserDetails userDetails) {
        return withUsername(userDetails.getUsername()).password(userDetails.getPassword()).accountExpired(!userDetails.isAccountNonExpired()).accountLocked(!userDetails.isAccountNonLocked()).authorities(userDetails.getAuthorities()).credentialsExpired(!userDetails.isCredentialsNonExpired()).disabled(!userDetails.isEnabled());
    }

    public static class UserBuilder {
        private String username;
        private String password;
        private List<GrantedAuthority> authorities;
        private boolean accountExpired;
        private boolean accountLocked;
        private boolean credentialsExpired;
        private boolean disabled;
        private Function<String, String> passwordEncoder;

        private UserBuilder() {
            this.passwordEncoder = (password) -> {
                return password;
            };
        }

        public User.UserBuilder username(String username) {
            Assert.notNull(username, "username cannot be null");
            this.username = username;
            return this;
        }

        public User.UserBuilder password(String password) {
            Assert.notNull(password, "password cannot be null");
            this.password = password;
            return this;
        }

        public User.UserBuilder passwordEncoder(Function<String, String> encoder) {
            Assert.notNull(encoder, "encoder cannot be null");
            this.passwordEncoder = encoder;
            return this;
        }

        public User.UserBuilder roles(String... roles) {
            List<GrantedAuthority> authorities = new ArrayList(roles.length);
            String[] var3 = roles;
            int var4 = roles.length;

            for(int var5 = 0; var5 < var4; ++var5) {
                String role = var3[var5];
                Assert.isTrue(!role.startsWith("ROLE_"), role + " cannot start with ROLE_ (it is automatically added)");
                authorities.add(new SimpleGrantedAuthority("ROLE_" + role));
            }

            return this.authorities((Collection)authorities);
        }

        public User.UserBuilder authorities(GrantedAuthority... authorities) {
            return this.authorities((Collection)Arrays.asList(authorities));
        }

        public User.UserBuilder authorities(Collection<? extends GrantedAuthority> authorities) {
            this.authorities = new ArrayList(authorities);
            return this;
        }

        public User.UserBuilder authorities(String... authorities) {
            return this.authorities((Collection)AuthorityUtils.createAuthorityList(authorities));
        }

        public User.UserBuilder accountExpired(boolean accountExpired) {
            this.accountExpired = accountExpired;
            return this;
        }

        public User.UserBuilder accountLocked(boolean accountLocked) {
            this.accountLocked = accountLocked;
            return this;
        }

        public User.UserBuilder credentialsExpired(boolean credentialsExpired) {
            this.credentialsExpired = credentialsExpired;
            return this;
        }

        public User.UserBuilder disabled(boolean disabled) {
            this.disabled = disabled;
            return this;
        }

        public UserDetails build() {
            String encodedPassword = (String)this.passwordEncoder.apply(this.password);
            return new User(this.username, encodedPassword, !this.disabled, !this.accountExpired, !this.credentialsExpired, !this.accountLocked, this.authorities);
        }
    }

    private static class AuthorityComparator implements Comparator<GrantedAuthority>, Serializable {
        private static final long serialVersionUID = 500L;

        private AuthorityComparator() {
        }

        public int compare(GrantedAuthority g1, GrantedAuthority g2) {
            if (g2.getAuthority() == null) {
                return -1;
            } else {
                return g1.getAuthority() == null ? 1 : g1.getAuthority().compareTo(g2.getAuthority());
            }
        }
    }
}
User.java
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.springframework.security.core.userdetails;

import java.io.Serializable;
import java.util.Collection;
import org.springframework.security.core.GrantedAuthority;

public interface UserDetails extends Serializable {
    Collection<? extends GrantedAuthority> getAuthorities();

    String getPassword();

    String getUsername();

    boolean isAccountNonExpired();

    boolean isAccountNonLocked();

    boolean isCredentialsNonExpired();

    boolean isEnabled();
}
UserDetails.java

    待更新......

猜你喜欢

转载自www.cnblogs.com/NeverCtrl-C/p/9030521.html
今日推荐