SpringSecurity的配置

文章目录

    • 1. SpringSecurity
      • 1.1 导包
      • 1.2 自定义账号和密码
      • 1.3 实现UserDetailsService
      • 1.4 HomeController
      • 1.5 SecurityConfig

1. SpringSecurity

1.1 导包

		<dependency>
			<groupId>org.springframework.bootgroupId>
			<artifactId>spring-boot-starter-securityartifactId>
		dependency>

导包之后,就会在控制台生成一个账号和密码,密码是随机的,每次重启之后都不同,而且账号的权限也很高。

SpringSecurity的配置_第1张图片

1.2 自定义账号和密码

public class User implements UserDetails {
    private int id;
    private String username;
    private String password;
    private String salt;
    private String email;
    private int type;
    private int status;
    private String activationCode;
    private String headerUrl;
    private Date createTime;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    //账号未过期
    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    //账号未锁定
    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    //凭证未过期
    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    //账号可用
    @Override
    public boolean isEnabled() {
        return false;
    }

    public void setUsername(String username) {
        List<GrantedAuthority> list = new ArrayList<>();
        this.username = username;
    }

    //调用这个方法可以知道该用户的权限
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        List<GrantedAuthority> list = new ArrayList<>();

        list.add(new GrantedAuthority() {
            @Override
            public String getAuthority() {
                switch (type){
                    case 1:
                        return "ADMIN";
                    default:
                        return "USER";
                }
            }
        });
        return list;
    }
}

1.3 实现UserDetailsService

@Service
public class UserService implements UserDetailsService {

    @Autowired
    private UserMapper userMapper;

    public User findUserByName(String username) {
        return userMapper.selectByName(username);
    }

    //根据用户名查询用户
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        return this.findUserByName(username);
    }
}

1.4 HomeController

@Controller
public class HomeController {

    @RequestMapping(path = "/index", method = RequestMethod.GET)
    public String getIndexPage(Model model) {
        // 认证成功后,结果会通过SecurityContextHolder存入SecurityContext中.
        Object obj = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        if (obj instanceof User) {
            model.addAttribute("loginUser", obj);
        }
        return "/index";
    }

    @RequestMapping(path = "/discuss", method = RequestMethod.GET)
    public String getDiscussPage() {
        return "/site/discuss";
    }

    @RequestMapping(path = "/letter", method = RequestMethod.GET)
    public String getLetterPage() {
        return "/site/letter";
    }

    @RequestMapping(path = "/admin", method = RequestMethod.GET)
    public String getAdminPage() {
        return "/site/admin";
    }

    @RequestMapping(path = "/loginpage", method = {RequestMethod.GET, RequestMethod.POST})
    public String getLoginPage() {
        return "/site/login";
    }

    // 拒绝访问时的提示页面
    @RequestMapping(path = "/denied", method = RequestMethod.GET)
    public String getDeniedPage() {
        return "/error/404";
    }
}

1.5 SecurityConfig

这个SpringSecurity的配置类,主要的配置都在这个完成的:

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserService userService;

    public void configure(WebSecurity web){
        //忽略静态资源的访问
        web.ignoring().antMatchers("/resources/**");
    }

    /**
     * 处理认证逻辑
     * AuthenticationManager : 认证的核心接口
     * AuthenticationManagerBuilder : 用于构建AuthenticationManager对象的工具
     * ProviderManager : AuthenticationManager接口的默认实现类
     */
    public void configure(AuthenticationManagerBuilder auth) throws Exception {

        /**
         * 自定义认证规则:和shiro的认证原理相同
         * ProviderManager持有一组AuthenticationProvider,每个AuthenticationProvider负责一种认证
         * 委托模式:让认证委托给AuthenticationProvider
         */
        auth.authenticationProvider(new AuthenticationProvider() {
            /**
             *
             * 获取认证信息:SpringSecurity做认证
             * Authentication:用来封装认证信息的接口,不同的实现类代表不同类型的认证信息
             * authentication:封装了前端登录传入的用户名和密码
             */
            @Override
            public Authentication authenticate(Authentication authentication) throws AuthenticationException {
                //获取username
                String username = authentication.getName();
                //获取password
                String password = (String)authentication.getCredentials();

                //根据用户名到数据库中查询用户信息
                User user = userService.findUserByName(username);
                if(user==null){
                    throw new UsernameNotFoundException("账号不存在");
                }

                password = CommunityUtil.md5(password+user.getSalt());
                if(!user.getPassword().equals(password)){
                    throw new BadCredentialsException("密码不正确");
                }

                //返回认证的结果:主要信息,证书,权限
                return new UsernamePasswordAuthenticationToken(user,user.getPassword(),user.getAuthorities());
            }

            /**
             * 当前AuthenticationProvider支持哪种认证类型
             */
            @Override
            public boolean supports(Class<?> aClass) {
                //Authentication接口的常用实现类,即支持账号密码的认证
                return UsernamePasswordAuthenticationToken.class.equals(aClass);
            }
        });
    }

    public void configure(HttpSecurity http) throws Exception{
        //登录相关的配置
        http.formLogin()
                .loginPage("/loginpage")//登录页面
                .loginProcessingUrl("/login")//处理登录请求的路径
                .successHandler(new AuthenticationSuccessHandler() {
                    @Override
                    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
                        //重定向:重定向是客户端行为
                        //服务器A给浏览器做出相应后,浏览器再次发送一个请求到服务器B,两次不同的请求
                        response.sendRedirect(request.getContextPath()+"/index");
                    }
                })
                .failureHandler(new AuthenticationFailureHandler() {
                    @Override
                    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException {
                        //将错误绑定到request上,转发到登录页面
                        //同一个请求,服务器A将一个请求转发给另一个服务器B处理
                        //转发是服务器行为
                        request.setAttribute("error",e.getMessage());
                        request.getRequestDispatcher("/loginpage").forward(request,response);
                    }
                });

        //登出相关的配置
        http.logout()
                .logoutUrl("/logout")
                .logoutSuccessHandler(new LogoutSuccessHandler() {
                    @Override
                    public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
                        response.sendRedirect(request.getContextPath()+"/index");
                    }
                });

        //授权配置
        http.authorizeRequests()
                .antMatchers("/letter").hasAnyAuthority("USER","ADMIN")//私信的权限
                .antMatchers("/admin").hasAnyAuthority("ADMIN")
                .and()
                .exceptionHandling().accessDeniedPage("/denied");//没有权限时返回的页面

        // 验证码配置:增加filter,验证码应该在验证用户名和密码之前进行验证
        http.addFilterBefore(new Filter() {
            @Override
            public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
                HttpServletRequest request = (HttpServletRequest) servletRequest;
                HttpServletResponse response = (HttpServletResponse) servletResponse;
                if(request.getServletPath().equals("/login")){
                    String verifyCode = request.getParameter("verifyCode");
                    //假设我们的验证码固定为1234
                    if(verifyCode==null || verifyCode.equalsIgnoreCase("1234")){
                        request.setAttribute("error","验证码错误");
                        request.getRequestDispatcher("/loginpage").forward(request,response);
                        return;
                    }
                }
                //验证码正确,让请求继续向下执行
                filterChain.doFilter(request,response);
            }
        }, UsernamePasswordAuthenticationFilter.class);

        //记住我配置
        http.rememberMe()
                .tokenRepository(new InMemoryTokenRepositoryImpl())//将用户信息记录内存中(也可以数据库中)
                .tokenValiditySeconds(3600*24)//token的过期时间
                .userDetailsService(userService);//通过UserService查询用户信息,自动进行认证
    }
}

你可能感兴趣的:(SpringSecurity)