1:首先要有实体表等

springboot前后端账号密码保存在哪 springboot登录接口_spring


springsecurity使用接口UserService中的loadUserByUsername(String username);查询数据库中的用户信息,所以我们需要自定义一个UserServiceImpl来实现UserService,重写loadUserByUsername(String username)方法。springsecurity需要用UserDetails来实现认证和授权。所以我写了一个类来实现UserDetails这个接口,并且将用户实体作为里边的一个属性。

UserDetailsService实现

package com.guizhou.springsecurity;

import com.guizhou.springsecurity.entity.UserDetail;
import com.guizhou.springsecurity.service.UserService;
import lombok.extern.slf4j.Slf4j;
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.Service;

import javax.annotation.Resource;

/**
 * @Description:
 * @auther: libiao
 * @Email: libiao@163.com
 * @Date: 2020-6-17 10:35
 * @Copyright: (c) 2019-2022  XXXX公司
 */
@Service
@Slf4j
public class UserSecurityServiceImpl implements UserDetailsService {

    @Resource
    UserService userService;

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

        log.info("login:{}", username);

        UserDetail user = userService.loadUserByUsername(username);
        if(user == null){
            throw new UsernameNotFoundException("未找到");
        }

        return user;
    }
}

UserDetails的实现

package com.guizhou.springsecurity.entity;

import lombok.Data;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.util.CollectionUtils;

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

/**
 * @author xxxx
 */
@Data
public class UserDetail implements UserDetails {
	// 这个才是用户实体
    protected SysUser sysUser;

    /**
     * @Description: 装配角色
     * @author libiao
     * @Date 2020-6-17 17:45
     * @Param
     * @return
     **/
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
        if (sysUser == null) {
            return grantedAuthorities;
        }
        List<SysRole> sysRoles = sysUser.getSysRoles();
        if (!CollectionUtils.isEmpty(sysRoles)) {
            for (SysRole sysRole : sysRoles) {
                String code = sysRole.getCode();
                grantedAuthorities.add(new SimpleGrantedAuthority(code));
            }
        }
        return grantedAuthorities;
    }

    @Override
    public String getPassword() {
        return sysUser == null ? "" : sysUser.getPassword();
    }

    @Override
    public String getUsername() {
        return sysUser == null ? "" : sysUser.getUsername();
    }

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

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

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

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

还自定义了认证成功的Handler和失败的Handler

package com.guizhou.springsecurity.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
import org.springframework.stereotype.Component;

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

/**
 * @Description:
 * @auther: libiao
 * @Email: libiao@163.com
 * @Date: 2020-6-17 15:41
 * @Copyright: (c) 2019-2022  XXXX公司
 */
@Component
@Slf4j
public class MyAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws ServletException, IOException {
        log.info("登陆成功:" + authentication);
        super.onAuthenticationSuccess(request, response, authentication);
    }
}
package com.guizhou.springsecurity.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.DefaultRedirectStrategy;
import org.springframework.security.web.RedirectStrategy;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import org.springframework.stereotype.Component;

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

/**
 * @Description:
 * @auther: libiao
 * @Email: libiao@163.com
 * @Date: 2020-6-17 15:44
 * @Copyright: (c) 2019-2022  XXXX公司
 */
@Component
@Slf4j
public class MyAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {


    /*@Autowired
    public MyAuthenticationFailureHandler() {
        super("/");
    }*/

    private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();

    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
        log.info("登陆失败!" + exception);
        super.saveException(request, exception);
        if (exception instanceof UsernameNotFoundException) {

        }
        // 重定向地址,上边注释的代码也可以实现(源码中体现)
        redirectStrategy.sendRedirect(request,response,"/toLoginPage");
        //super.onAuthenticationFailure(request, response, exception);
    }
}

springsecurityConfig中的代码

package com.guizhou.springsecurity.config;

import com.guizhou.springsecurity.UserSecurityServiceImpl;
import com.guizhou.springsecurity.utils.PasswordEncoderImpl;
import org.springframework.context.annotation.Bean;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
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.crypto.password.PasswordEncoder;
import org.springframework.util.DigestUtils;

import javax.annotation.Resource;


/**
 * @Description:
 * @auther: libiao
 * @Email: libiao@163.com
 * @Date: 2020-6-16 16:26
 * @Copyright: (c) 2019-2022  XXXX公司
 */
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Resource
    UserSecurityServiceImpl userSecurityService;

    @Resource
    MyAuthenticationFailureHandler myAuthenticationFailureHandler;

    @Resource
    MyAuthenticationSuccessHandler myAuthenticationSuccessHandler;



    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 这个是logout方法报404,关闭csrf跨站请求伪造。如果使用Post请求logout方法,就可以
         http.csrf().disable();
        http.authorizeRequests().antMatchers("/", "/index").permitAll()
                .antMatchers("/toVip/1").hasRole("vip1")
                .antMatchers("/toVip/2").hasRole("vip2")
                .antMatchers("/toVip/3").hasRole("vip3")
                .and().formLogin().loginPage("/toLoginPage").loginProcessingUrl("/login")
				// 失败和成功的handler
                .successForwardUrl("/").successHandler(myAuthenticationSuccessHandler).
                failureHandler(myAuthenticationFailureHandler)
        ;

        http.logout().logoutSuccessUrl("/");
        // 开启记住我,和记住的时间
  http.rememberMe().rememberMeParameter("rememberMe").tokenValiditySeconds(60)
                .and();
    }



// 加密,也是自定义实现
    @Bean
    public PasswordEncoder passwordEncoder(){
        return new PasswordEncoderImpl();
    }

// 因为springsecurity默认是隐藏了一些错误提示信息,
//AbstractUserDetailsAuthenticationProvider中的protected boolean hideUserNotFoundExceptions = true;,所以我们需要手动将这个属性设置为false
    @Bean
    public DaoAuthenticationProvider daoAuthenticationProvider(){
        DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
        // 必须要设置加密方式
        provider.setPasswordEncoder(passwordEncoder());
        // 必须要设置 userSecurityService
        provider.setUserDetailsService(userSecurityService);
        // 这个就是将错误信息放开
        provider.setHideUserNotFoundExceptions(false);
        return provider;
    }

}

将错误信息放开后,我们就可以根据实际情况提示更友善的信息,例如

// 查询的时候,用户为空
SysUser sysUser = sysUserMapper.loadUserByUsername(username);
        if (sysUser == null) {
            throw new UsernameNotFoundException(username + "未找到!");
        }
        // 或者用户被锁定
        throw new LockedException("用户被锁定了");
        具体的可以看AuthenticationException的实现类

因为我在自定义的失败的handler中设置过错误信息,所以可以直接在session中取

super.saveException(request, exception);

登陆页获取错误信息

<div id="message" style="color:red;" th:text="${session?.SPRING_SECURITY_LAST_EXCEPTION?.message}"></div>

遇到的坑:登陆页面中的用户名,密码的name一定要和config中的配置一样,用默认的就可以了(今天写demo的时候,手残把config中的名字改了,没有注意到。UserDetailsService中就一直拿不到前台传入的用户名)

springboot前后端账号密码保存在哪 springboot登录接口_java_02

启动报错:java.io.EOFException: null(我是百度的,博文地址)

可能原因是由于tomcat上次非正常关闭时有一些活动session被持久化(表现为一些临时文件),在重启时,tomcat尝试去恢复这些session的持久化数据但又读取失败造成的。EOFException表示输入过程中意外地到达文件尾或流尾的信号,导致从session中获取数据失败。此异常不影响系统的使用。

解决方法:

1.外部tomcat:

找到:tomcat/work/Catalina/localhost/项目名/SESSIONS.ser文件,将它删除

2.springboot内置tomcat

springboot内置tomcat路径不太好找,但是在报出这个异常时,控制台也会打印发生异常的文件路径,如下图

springboot前后端账号密码保存在哪 springboot登录接口_spring_03


这一行的完整信息为:

Unable to delete [C:\Users\ADMINI~1\AppData\Local\Temp\AEEC93AEB69DEF3532F47F7AED7494FA49E5EEF3\servlet-sessions\SESSIONS.ser] after reading the persisted sessions. The continued presence of this file may cause future attempts to persist sessions to fail.