第五章 基于数据库的认证


1、第一步则是先要实现UserDetails接口

主要用于定制化将数据库数据对接Security框架。


import com.example.springsecuritystudy4.entity.User;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

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

public class SecurityUser implements UserDetails {

    private final User user;

    private List<SimpleGrantedAuthority> authorityList;

    public SecurityUser(User user) {
        this.user = user;
    }

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

    public void setAuthorityList(List<SimpleGrantedAuthority> authorityList) {
        this.authorityList = authorityList;
    }

    @Override
    public String getPassword() {
        String pwd = user.getPassword();
        user.setPassword(null);
        return pwd;
    }

    @Override
    public String getUsername() {
        return user.getUsername();
    }

    /*代表账号是否已经过期(true账号没有过期,false账号已经过期)*/
    @Override
    public boolean isAccountNonExpired() {
        return user.getAccountNoExpired().equals(1);
    }
    
    /*代表账号是否被锁定(true没有锁定,false锁定)*/
    @Override
    public boolean isAccountNonLocked() {
        return user.getAccountNoLocked().equals(1);
    }
 /*代表凭证是否已经过期(true账号没有过期,false账号已经过期)*/
    @Override
    public boolean isCredentialsNonExpired() {
        return user.getCredentialsNoExpired().equals(1);
    }
 /*代表账号是否可用*/
    @Override
    public boolean isEnabled() {
        return true;
    }
}

2、实现UserDetailsService接口

实现UserDetailService接口的作用主要就是定制化用户登录时的数据获取方式以及对比方式等。


import com.example.springsecuritystudy4.entity.User;
import com.example.springsecuritystudy4.service.RoleMenuService;
import com.example.springsecuritystudy4.service.UserService;
import com.example.springsecuritystudy4.vo.SecurityUser;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
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;
import java.util.ArrayList;
import java.util.List;

@Service
public class SecurityUserDetailServiceImpl implements UserDetailsService {

    @Resource
    private UserService userService;
    @Resource
    private RoleMenuService roleMenuService;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User byUserName = userService.getByUserName(username);

        if(byUserName==null)
            throw new UsernameNotFoundException("该用户不存在");

        List<String> queryPermissionsByUserId = roleMenuService.queryPermissionsByUserId(byUserName.getUid());

        SecurityUser securityUser = new SecurityUser(byUserName);
        List<SimpleGrantedAuthority> authorityList = new ArrayList<>();
        queryPermissionsByUserId.stream().forEach((value)-> authorityList.add(new SimpleGrantedAuthority(value)));
        securityUser.setAuthorityList(authorityList);
        return securityUser;
    }
}

第六章 WebSecurity配置中关于其中登录页面等其他页面定制化



import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
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.PasswordEncoder;

@Slf4j
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

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

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .anyRequest()
                .authenticated();

        http.formLogin()
                .loginPage("/toLogin") // 配置登录页面
                .usernameParameter("uname") // 用户名参数
                .passwordParameter("pwd") // 密码参数
                .loginProcessingUrl("/login/doLogin")  //单击登录后进入url
                .failureForwardUrl("/toLogin") // 登录失败
                .successForwardUrl("/toIndex") //登录成功
                .permitAll();  //配置登录

        http.logout().logoutSuccessUrl("/toLogin");  //配置退出成功登录页面
        http.csrf().disable(); //关闭跨域请求保护
    }
}

第七章 验证码整合


验证码生成工具可以使用hutool依赖库进行。

验证码生成接口代码


import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.CircleCaptcha;
import cn.hutool.captcha.generator.MathGenerator;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Slf4j
@Controller
public class CaptchaController {

    @GetMapping(value = "code/getCaptchaCode")
    public void getCaptchaCode(HttpServletRequest httpServletRequest,
                               HttpServletResponse httpServletResponse) throws IOException {
        //ShearCaptcha shearCaptcha = CaptchaUtil.createShearCaptcha(200, 100, 4, 5);
        CircleCaptcha circleCaptcha = CaptchaUtil.createCircleCaptcha(200, 100, 4, 1000);
        circleCaptcha.setGenerator(new MathGenerator());
        String code = circleCaptcha.getCode();
        log.info("生成的图片验证码为:{}",code);
        //将验证码存储到session中
        httpServletRequest.getSession().setAttribute("CAPTCHA CODE",code);
        httpServletResponse.setContentType("image/png");
        //将图片写到响应流里
        ImageIO.write(circleCaptcha.getImage(),"PNG",httpServletResponse.getOutputStream());
    }
}

写好验证码之后即可在WebSecurity实现配置类中将验证码接口放开:


import com.example.springsecuritystudy4.filter.ValidateCodeFilter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
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.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.annotation.Resource;

@Slf4j
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

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

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .mvcMatchers("/code/getCaptchaCode") //将验证码接口放开
                .permitAll()
                .anyRequest()
                .authenticated();

        http.formLogin()
                .loginPage("/toLogin") // 配置登录页面
                .usernameParameter("uname") // 用户名参数
                .passwordParameter("pwd") // 密码参数
                .loginProcessingUrl("/login/doLogin")  //单击登录后进入url
                .failureForwardUrl("/toLogin") // 登录失败
                .successForwardUrl("/toIndex") //登录成功
                .permitAll();  //放开

        http.logout().logoutSuccessUrl("/toLogin");  //配置退出成功登录页面
        http.csrf().disable(); //关闭跨域请求保护
    }
}

利用过滤器来验证验证码