spring boot security 安全权限 ---2 短信图片验证码 认证授权

spring boot security 安全权限 ---2 短信图片验证码 认证授权,第1张

spring boot security 安全权限 ---2 短信/图片验证码 认证授权

二、spring security 短信验证 认证授权

A、自定义图片验证码验证

         1、基于spring security重写图片验证码验证的过滤器ImgCodeFilter

package com.example.springsecurity.filter;

import com.example.springsecurity.exception.ImgException;
import com.example.springsecurity.handler.MyAuthenticationFailureHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

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


@Component
public class ImgCodeFilter extends oncePerRequestFilter {
    @Autowired
    MyAuthenticationFailureHandler authenticationFailureHandler;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        //从请求中获取请求地址和方式进行判断是否是登录请求验证图片验证码
        if("/login".equals(request.getRequestURI())&&"post".equalsIgnoreCase(request.getMethod())){
            try{
                verityCode(request);
            }catch (ImgException e){
                authenticationFailureHandler.onAuthenticationFailure(request,response,e);
            }
        }
        doFilter(request,response,filterChain);
    }

    //验证图片验证码
    public void verityCode(HttpServletRequest request) throws ImgException {
        //图片验证码的在页面显示需要调用生成图片验证码的工具类,验证码生成后会先存入redis,在此略
        //这里的1234是自定义的,在实际开发中是从redis获取
        if(!"1234".equals(request.getParameter("code"))){
            throw new ImgException("验证码错误");
        }
    }
}

  2、重写登录异常

package com.example.springsecurity.exception;

import org.springframework.security.core.AuthenticationException;


public class ImgException extends AuthenticationException {

    public ImgException(String explanation) {
        super(explanation);
    }
}

3、SecurityConfig中注入过滤器,并把过滤器加入security

@Autowired
private ImgCodeFilter imgCodeFilter;
 //验证用户名密码之前进行过滤验证
 http.addFilterBefore(imgCodeFilter, UsernamePasswordAuthenticationFilter.class);

4、修改登录失败异常处理

if(e instanceof LockedException){
            map.put("msg","账户被锁定,无法登录");
        }else if(e instanceof BadCredentialsException){
            map.put("msg","用户名或密码错误");
        }else if(e instanceof DisabledException){
            map.put("msg","账户被禁用,无法登录");
        }else if(e instanceof AccountExpiredException){
            map.put("msg","账户已过期,无法登录");
        }else if(e instanceof CredentialsExpiredException){
            map.put("msg","密码已过期,无法登录");
        }else if(e instanceof ImgException){
            map.put("msg",e.getMessage());
        }else{
            map.put("msg","登录异常,请联系管理员");
        }

5、登录html加入图形验证码

B、短信验证码登录开发

   

1、重写AbstractAuthenticationToken中的方法

package com.example.springsecurity.handler;

import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;

import java.util.Collection;


public class SmsCodeAuthenticationToken extends AbstractAuthenticationToken {
    private static final long serialVersionUID = 500L;
    private final Object principal;

    public SmsCodeAuthenticationToken(Object mobile) {
        super((Collection)null);
        this.principal = mobile;
        this.setAuthenticated(false);
    }

    public SmsCodeAuthenticationToken(Object principal, Collection authorities) {
        super(authorities);
        this.principal = principal;
        super.setAuthenticated(true);
    }

    public Object getCredentials() {
        return null;
    }

    public Object getPrincipal() {
        return this.principal;
    }

    public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
        if (isAuthenticated) {
            throw new IllegalArgumentException("Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead");
        } else {
            super.setAuthenticated(false);
        }
    }

    public void eraseCredentials() {
        super.eraseCredentials();
    }

}

2、重写AbstractAuthenticationProcessingFilter中的方法

package com.example.springsecurity.handler;

import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;

import java.util.Collection;


public class SmsCodeAuthenticationToken extends AbstractAuthenticationToken {
    private static final long serialVersionUID = 500L;
    private final Object principal;

    public SmsCodeAuthenticationToken(Object mobile) {
        super((Collection)null);
        this.principal = mobile;
        this.setAuthenticated(false);
    }

    public SmsCodeAuthenticationToken(Object principal, Collection authorities) {
        super(authorities);
        this.principal = principal;
        super.setAuthenticated(true);
    }

    public Object getCredentials() {
        return null;
    }

    public Object getPrincipal() {
        return this.principal;
    }

    public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
        if (isAuthenticated) {
            throw new IllegalArgumentException("Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead");
        } else {
            super.setAuthenticated(false);
        }
    }

    public void eraseCredentials() {
        super.eraseCredentials();
    }

}

3、重写AbstractAuthenticationProcessingFilter中的方法

package com.example.springsecurity.filter;

import com.example.springsecurity.handler.SmsCodeAuthenticationToken;
import org.springframework.lang.Nullable;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


public class SmsCodeAuthenticationFilter extends AbstractAuthenticationProcessingFilter  {

    public static final String SPRING_SECURITY_FORM_MOBILE_KEY = "mobile";
    private String mobileParameter = "mobile";
    private boolean postonly = true;

    public SmsCodeAuthenticationFilter() {
        super(new AntPathRequestMatcher("/mobile", "POST"));
    }

    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        if (this.postonly && !request.getMethod().equals("POST")) {
            throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
        } else {
            String mobile = this.obtainMobile(request);
            if (mobile == null) {
                mobile = "";
            }
            mobile = mobile.trim();
            SmsCodeAuthenticationToken authRequest = new SmsCodeAuthenticationToken(mobile);
            this.setDetails(request, authRequest);
            return this.getAuthenticationManager().authenticate(authRequest);
        }
    }

    @Nullable
    protected String obtainMobile(HttpServletRequest request) {
        return request.getParameter(this.mobileParameter);
    }

    protected void setDetails(HttpServletRequest request, SmsCodeAuthenticationToken authRequest) {
        authRequest.setDetails(this.authenticationDetailsSource.buildDetails(request));
    }

    public void setMobileParameter(String mobileParameter) {
        Assert.hasText(mobileParameter, "Username parameter must not be empty or null");
        this.mobileParameter = mobileParameter;
    }

    public void setPostonly(boolean postOnly) {
        this.postonly = postOnly;
    }

    public final String getMobileParameter() {
        return this.mobileParameter;
    }


}

4、重写AuthenticationProvider 中的方法

package com.example.springsecurity.handler;

import com.example.springsecurity.service.UserService;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;


public class SmsCodeAuthenticationProvider implements AuthenticationProvider {


    private UserService userService;

    public UserService getUserService() {
        return userService;
    }

    public void setUserService(UserService userService) {
        this.userService = userService;
    }

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        SmsCodeAuthenticationToken smsCodeAuthenticationToken = (SmsCodeAuthenticationToken)authentication;
        UserDetails user = userService.loadUserByUsername((String)smsCodeAuthenticationToken.getPrincipal());
        if (user == null) {
            throw new InternalAuthenticationServiceException("无法获取用户信息");
        }
        //构造认证结果
        SmsCodeAuthenticationToken result = new SmsCodeAuthenticationToken(user, user.getAuthorities());
        result.setDetails(smsCodeAuthenticationToken.getDetails());
        return result;
    }

    @Override
    public boolean supports(Class authentication) {
        return SmsCodeAuthenticationToken.class.isAssignableFrom(authentication);
    }


}

5、短信验证码过滤器SmsCodeFilter,用于验证短信验证码是否正确

package com.example.springsecurity.filter;

import com.example.springsecurity.exception.SmsCodeException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;


@Component
public class SmsCodeFilter extends oncePerRequestFilter implements InitializingBean {

    @Autowired
    private AuthenticationFailureHandler authenticationFailureHandler;

    private Set urls = new HashSet<>();

    private AntPathMatcher antPathMatcher = new AntPathMatcher();

    @Override
    public void afterPropertiesSet() throws ServletException {
        super.afterPropertiesSet();
        // 这里配置需要拦截的地址
        urls.add("/mobile");

    }

    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) 
throws ServletException, IOException {
        boolean action = false;
        //判断请求地址
        for (String url : urls) {
            if (antPathMatcher.match(url, httpServletRequest.getRequestURI())) {
                action = true;
                break;
            }
        }
        if (action) {
            try {
                validate(httpServletRequest);
            } catch (SmsCodeException e) {
                authenticationFailureHandler.onAuthenticationFailure(httpServletRequest, httpServletResponse, e);
                return;
            }
        }
        filterChain.doFilter(httpServletRequest, httpServletResponse);
    }

    private void validate(HttpServletRequest request) {
       String code= (String) request.getSession().getAttribute("code");
        String smsCodeRequest = request.getParameter("smsCode");
        if (code == null) {
            throw new SmsCodeException("短信验证码不存在");
        }
        if(!smsCodeRequest.equalsIgnoreCase(code)) {
            throw new SmsCodeException("短信验证码错误");
        }
        //清除session
//        request.getSession().removeAttribute("code");
    }

}

6、SmsCodeAuthenticationSecurityConfig 短信验证登录处理中心

package com.example.springsecurity.config;

import com.example.springsecurity.filter.SmsCodeAuthenticationFilter;
import com.example.springsecurity.handler.MyAuthenticationFailureHandler;
import com.example.springsecurity.handler.MyAuthenticationSuccessHandler;
import com.example.springsecurity.handler.SmsCodeAuthenticationProvider;
import com.example.springsecurity.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.SecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.DefaultSecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.stereotype.Component;


@Component
public class SmsCodeAuthenticationSecurityConfig extends SecurityConfigurerAdapter {
    @Autowired
    private MyAuthenticationFailureHandler myAuthenticationFailHandler;

    @Autowired
    private MyAuthenticationSuccessHandler myAuthenticationSuccessHandler;

    @Autowired
    private UserService userService;

    @Override
    public void configure(HttpSecurity http) throws Exception {

        SmsCodeAuthenticationFilter smsCodeAuthenticationFilter = new SmsCodeAuthenticationFilter();
        smsCodeAuthenticationFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));
        smsCodeAuthenticationFilter.setAuthenticationSuccessHandler(myAuthenticationSuccessHandler);
        smsCodeAuthenticationFilter.setAuthenticationFailureHandler(myAuthenticationFailHandler);

        SmsCodeAuthenticationProvider smsCodeAuthenticationProvider = new SmsCodeAuthenticationProvider();
        smsCodeAuthenticationProvider.setUserService(userService);

        http.authenticationProvider(smsCodeAuthenticationProvider)
               .addFilterBefore(smsCodeAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); 
    }

}

7、SecurityConfig配置

package com.example.springsecurity.config;

import com.example.springsecurity.domain.Auth;
import com.example.springsecurity.filter.ImgCodeFilter;
import com.example.springsecurity.filter.SmsCodeFilter;
import com.example.springsecurity.handler.MyAuthenticationFailureHandler;
import com.example.springsecurity.handler.MyAuthenticationSuccessHandler;
import com.example.springsecurity.mapper.AuthMapper;
import com.example.springsecurity.service.UserService;
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.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.configurers.expressionUrlAuthorizationConfigurer;
import org.springframework.security.core.Authentication;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.stereotype.Component;

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


@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
    @Autowired
    private MyAuthenticationSuccessHandler successHandler;

    
    @Autowired
    private MyAuthenticationFailureHandler failureHandler;

    
    @Autowired
    private UserService userService;

    @Autowired
    private SmsCodeFilter smsCodeFilter;

    @Autowired
    private SmsCodeAuthenticationSecurityConfig codeAuthenticationSecurityConfig;

    //配置加密的方式
    @Bean
    PasswordEncoder passwordEncoder() {
        return NoOpPasswordEncoder.getInstance();
    }

    //配置认证用户信息和授权
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userService);
    }

    //配置拦截请求资源
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                //开启HttpSecurity配置
                .authorizeRequests()
                //指定路径
                .antMatchers("/login.html").permitAll()
                .antMatchers("/sendCode").permitAll()
                .antMatchers("/mobile").permitAll()
                .antMatchers("
@Component
public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
    @Override
    public void onAuthenticationSuccess(HttpServletRequest req, HttpServletResponse resp, Authentication auth) throws IOException, ServletException {
        //这里可以进行页面的跳转或返回json数据给客户端浏览器
        User principal = (User) auth.getPrincipal();//获取登录用户的信息
        principal.setPassword(null);
        resp.setContentType("application/json;charset=utf-8");
        PrintWriter out=resp.getWriter();
        resp.setStatus(200);
        Map map=new HashMap<>();
        map.put("status",200);
        map.put("msg",principal);
        ObjectMapper objectMapper = new ObjectMapper();
        out.write(objectMapper.writevalueAsString(map));
        out.flush();;
        out.close();
//        resp.sendRedirect("/");
    }
}

package com.example.springsecurity.handler;

import com.example.springsecurity.exception.ImgException;
import com.example.springsecurity.exception.SmsCodeException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.security.authentication.*;
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;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;



@Component
public class MyAuthenticationFailureHandler implements AuthenticationFailureHandler {
    @Override
    public void onAuthenticationFailure(HttpServletRequest req, HttpServletResponse resp, AuthenticationException e) throws IOException, ServletException {
        resp.setContentType("application/json;charset=utf-8");
        PrintWriter out=resp.getWriter();
        resp.setStatus(401);
        Map map=new HashMap<>();
        map.put("status",401);
        if(e instanceof LockedException){
            map.put("msg","账户被锁定,无法登录");
        }else if(e instanceof BadCredentialsException){
            map.put("msg","用户名或密码错误");
        }else if(e instanceof DisabledException){
            map.put("msg","账户被禁用,无法登录");
        }else if(e instanceof AccountExpiredException){
            map.put("msg","账户已过期,无法登录");
        }else if(e instanceof CredentialsExpiredException){
            map.put("msg","密码已过期,无法登录");
        }else if(e instanceof SmsCodeException){
            map.put("msg",e.getMessage());
        }else{
            map.put("msg","登录异常,请联系管理员");
        }
        ObjectMapper objectMapper = new ObjectMapper();
        out.write(objectMapper.writevalueAsString(map));
        out.flush();;
        out.close();
//        resp.sendRedirect("/login.html");
    }
}

package com.example.springsecurity.exception;

import org.springframework.security.core.AuthenticationException;
import org.springframework.stereotype.Component;


public class SmsCodeException  extends AuthenticationException {
    public SmsCodeException(String msg) {
        super(msg);
    }
}

 9、登录页面login.html

10、短信发送的模拟接口

package com.example.springsecurity.controller;

import com.example.springsecurity.util.CodeUtil;
import com.example.springsecurity.util.SendSms;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.http.HttpServletRequest;

@Controller
public class UserController {
    @RequestMapping("/sendCode")
    public void sendCode(HttpServletRequest request,String mobile){
        String code = CodeUtil.getCode(6);
        System.out.println("验证码:"+code);
//        SendSms.sendMsg(mobile,code);
       request.getSession().setAttribute("code",code);
    }
}

 11、创建UserService类,重写验证的方法loadUserByUsername。

package com.example.springsecurity.service;

import com.example.springsecurity.domain.Auth;
import com.example.springsecurity.domain.User;
import com.example.springsecurity.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
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 java.util.ArrayList;
import java.util.List;


@Service
public class UserService implements UserDetailsService {
    @Autowired
    private UserMapper mapper;

    @Override
    public UserDetails loadUserByUsername(String mobile) throws UsernameNotFoundException {
        User user=mapper.loadUserByMobile(mobile);
        if(user==null){
            throw  new UsernameNotFoundException("用户不存在");
        }
        return user;
    }

}

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5695331.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存