spring boot 2.x 使用spring security 进行用户登录校验(一)

在用会了shiro后,最近学习使用spring security进行用户登录校验。

项目说明:

1、项目前后端分离架构。

2、后续想加上oauth2进行单点登录或用户中心。

(一)、spring security 主要用到的类和接口介绍

(二)、pom.xml加入依赖包

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

(三)、 几个重要类和接口的实现

package com.qwer.wen.common.auth;

import com.qwer.wen.common.auth.user.MD5AndAaltPasswordEncoder;
import com.qwer.wen.system.service.impl.UserDetailsServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

/**
 * @ClassName SpringSecurityConfig
 * @Description:
 * @Author ZHOUWEN
 * @Date 2020/4/22  22:05
 **/
@Configuration
@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    @Qualifier("userDetailsServiceImpl")
    private UserDetailsService userDetailsService;
    @Autowired
    @Qualifier("UserAuthenticationSuccessHandler")
    private UserAuthenticationSuccessHandler userAuthenticationSuccessHandler;
    @Autowired
    @Qualifier("UserAuthenticationFailureHandler")
    private UserAuthenticationFailureHandler userAuthenticationFailureHandler;



    /*
    * 对每个请求进行详细的安全性控制在于重载configure(HttpSecurity)方法,它为不同的url路径都有选择地应用安全性
    * */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        System.out.println("进入spring security.........");
        http.authorizeRequests()
                .anyRequest().authenticated().and()
                .formLogin()
                .successHandler(userAuthenticationSuccessHandler)
                .failureHandler(userAuthenticationFailureHandler)
                .loginProcessingUrl("/login").permitAll().and()
                //.httpBasic().and().successHandler(null).failureHandler(null).and()
                .logout().permitAll().and()
                //同一账号同时登录最大用户数maximumSessions(1)
                .sessionManagement().maximumSessions(1);
    }
    //从数据库中读取用户数据出来,指定密码的加密方式
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        System.out.println("验证用户......");
        auth.userDetailsService(userDetailsService).passwordEncoder(new MD5AndAaltPasswordEncoder());

    }
}
package com.qwer.wen.system.service.impl;

import com.qwer.wen.common.auth.user.UserDetailsDto;
import com.qwer.wen.common.spring.SpringUtil;
import com.qwer.wen.common.utils.bean.PojoUtils;
import com.qwer.wen.system.service.SystemUserService;
import com.qwer.wen.user.dao.pojo.TbUser;
import com.qwer.wen.user.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

/**
 * @ClassName UserDetailsServiceImpl
 * @Description:
 * @Author ZHOUWEN
 * @Date 2020/4/25  16:27
 **/
@Service("userDetailsServiceImpl")
public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    UserService userService;

    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
        System.out.println("进入loadUserByUsername:"+s);
        TbUser userByLoginUser = userService.getUserByLoginUser(s);
        UserDetailsDto userDetails = new UserDetailsDto();
        //将userByLoginUser 赋值给 userDetails
        userDetails.setLoginPassword(userByLoginUser.getLoginPassword());
        userDetails.setLoginUser(userByLoginUser.getLoginUser());
        return userDetails;

    }
}
package com.qwer.wen.common.auth.user;

import io.netty.buffer.Unpooled;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import java.util.Collection;
import java.util.List;

/**
 * @ClassName UserDetailsDto
 * @Description:
 * @Author ZHOUWEN
 * @Date 2020/4/25  16:32
 **/
public class UserDetailsDto implements UserDetails {

    private String loginUser;

    private String loginPassword;

    Collection<? extends GrantedAuthority> grantedAuthorities;

    boolean isAccountNonExpired  = true;
    boolean isAccountNonLocked  = true;
    boolean isCredentialsNonExpired  = true;
    boolean isEnabled  = true;

    public UserDetailsDto(){

    }

    public UserDetailsDto(String loginUser,String loginPassword,Collection<? extends GrantedAuthority> grantedAuthorities
    ,boolean isAccountNonExpired,boolean isAccountNonLocked,boolean isCredentialsNonExpired ,boolean isEnabled){
        //用户名和密码
        this.loginUser = loginUser;
        this.loginPassword= loginPassword;
        //各种标记
        this.isAccountNonExpired=isAccountNonExpired;
        this.isAccountNonLocked=isAccountNonLocked;
        this.isCredentialsNonExpired = isCredentialsNonExpired;
        this.isEnabled = isEnabled;
        //权限
        this.grantedAuthorities =grantedAuthorities;
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return null;
    }

    @Override
    public String getPassword() {
        return loginPassword;
    }

    @Override
    public String getUsername() {
        return loginUser;
    }

    @Override
    public boolean isAccountNonExpired() {
        return isAccountNonExpired;
    }

    @Override
    public boolean isAccountNonLocked() {
        return isAccountNonLocked;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return isCredentialsNonExpired;
    }

    @Override
    public boolean isEnabled() {
        return isEnabled;
    }

    public String getLoginUser() {
        return loginUser;
    }

    public void setLoginUser(String loginUser) {
        this.loginUser = loginUser;
    }

    public String getLoginPassword() {
        return loginPassword;
    }

    public void setLoginPassword(String loginPassword) {
        this.loginPassword = loginPassword;
    }
}
package com.qwer.wen.common.auth.user;

import org.apache.commons.codec.binary.Hex;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Random;

/**
 * @ClassName MD5AndAaltPasswordEncoder
 * @Description:
 * @Author ZHOUWEN
 * @Date 2020/4/27  23:05
 **/
public class MD5AndAaltPasswordEncoder implements PasswordEncoder {
    //加密
    @Override
    public String encode(CharSequence charSequence) {
        return MD5andSAL(charSequence.toString());
    }

    //比对
    @Override
    public boolean matches(CharSequence charSequence, String encoderPassword) {
        return verify(charSequence.toString(),encoderPassword);
    }

    /**
     * 普通MD5
     */
    public static String MD5(String input) {
        MessageDigest md5 = null;
        try {
            md5 = MessageDigest.getInstance("MD5");
        } catch (NoSuchAlgorithmException e) {
            return "check jdk";
        } catch (Exception e) {
            e.printStackTrace();
            return "";
        }
        char[] charArray = input.toCharArray();
        byte[] byteArray = new byte[charArray.length];

        for (int i = 0; i < charArray.length; i++)
            byteArray[i] = (byte) charArray[i];
        byte[] md5Bytes = md5.digest(byteArray);
        StringBuffer hexValue = new StringBuffer();
        for (int i = 0; i < md5Bytes.length; i++) {
            int val = ((int) md5Bytes[i]) & 0xff;
            if (val < 16)
                hexValue.append("0");
            hexValue.append(Integer.toHexString(val));
        }
        return hexValue.toString();
    }


    /**
     * 加盐MD5
     * @author daniel
     * @time 2016-6-11 下午8:45:04
     * @param password
     * @return
     */
    public static String MD5andSAL(String password) {
        Random r = new Random();
        StringBuilder sb = new StringBuilder(16);
        sb.append(r.nextInt(99999999)).append(r.nextInt(99999999));
        int len = sb.length();
        if (len < 16) {
            for (int i = 0; i < 16 - len; i++) {
                sb.append("0");
            }
        }
        String salt = sb.toString();
        password = MD5(password + salt);
        char[] cs = new char[48];
        for (int i = 0; i < 48; i += 3) {
            cs[i] = password.charAt(i / 3 * 2);
            char c = salt.charAt(i / 3);
            cs[i + 1] = c;
            cs[i + 2] = password.charAt(i / 3 * 2 + 1);
        }
        return new String(cs);
    }
    /**
     * 校验加盐后是否和原文一致
     * @author daniel
     * @time 2016-6-11 下午8:45:39
     * @param password
     * @param DBMD5
     * @return
     */
    public static boolean verify(String password, String DBMD5) {
        char[] cs1 = new char[32];
        char[] cs2 = new char[16];
        for (int i = 0; i < 48; i += 3) {
            cs1[i / 3 * 2] = DBMD5.charAt(i);
            cs1[i / 3 * 2 + 1] = DBMD5.charAt(i + 2);
            cs2[i / 3] = DBMD5.charAt(i + 1);
        }
        String salt = new String(cs2);
        return md5Hex(password + salt).equals(new String(cs1));
    }

    /**
     * 获取十六进制字符串形式的MD5摘要
     */
    private static String md5Hex(String src) {
        try {
            MessageDigest md5 = MessageDigest.getInstance("MD5");
            byte[] bs = md5.digest(src.getBytes());
            return new String(new Hex().encode(bs));
        } catch (Exception e) {
            return null;
        }
    }
}
package com.qwer.wen.common.auth;

import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.stereotype.Component;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @ClassName UserAuthenticationFailureHandler
 * @Description:
 * @Author ZHOUWEN
 * @Date 2020/4/28  22:45
 **/
@Component("UserAuthenticationFailureHandler")
public class UserAuthenticationFailureHandler implements AuthenticationFailureHandler {
    @Override
    public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
        System.out.println( httpServletRequest.getRequestURI());
        System.out.println("登录失败");
        httpServletResponse.setContentType("text/html;charset=UTF-8");
        httpServletResponse.setCharacterEncoding("UTF-8");
        httpServletResponse.getWriter().write("我试试登录失败。。。。。处理器");
    }
}
package com.qwer.wen.common.auth;

import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @ClassName UserAuthenticationSuccessHandler
 * @Description: 登录成功
 * @Author ZHOUWEN
 * @Date 2020/4/25  10:11
 **/
@Component("UserAuthenticationSuccessHandler")
public class UserAuthenticationSuccessHandler implements AuthenticationSuccessHandler {

    @Override
    public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
        System.out.println( httpServletRequest.getRequestURI());
        System.out.println("登录成功");
        httpServletResponse.setContentType("text/html;charset=UTF-8");
        httpServletResponse.setCharacterEncoding("UTF-8");
        httpServletResponse.getWriter().write("我试试登录成功。。。。。处理器");
    }
}

(四)登录后

前后端分离后我们返回登录后的地址即可。我这还没实现前后端分离。 

猜你喜欢

转载自blog.csdn.net/qq_30264689/article/details/105849280