springboot security 安全机制

springboot security 安全机制

 

认证流程:

1、由认证配置WebSecurityConfigurerAdapter的configure(HttpSecurity http)方法进入,添加拦截器addFilterBefore
2、进入拦截器AbstractAuthenticationProcessingFilter的attemptAuthentication方法,指定认证对象AbstractAuthenticationToken
3、进入认证逻辑AuthenticationProvider,根据supports的断定对认证的目标对象指定对哪个拦截器进行认证,进入具体的认证逻辑方法authenticate
4、认证结果:认证成功后进入拦截器的successfulAuthentication方法;认证失败后进入拦截器的unsuccessfulAuthentication方法
5、对认证结果进行处理
  a.认证成功的逻辑:进入SimpleUrlAuthenticationSuccessHandler的onAuthenticationSuccess方法
  b.认证失败的逻辑:进入SimpleUrlAuthenticationFailureHandler的onAuthenticationFailure方法
6、返回结果给页面,将数据封装在ObjectMapper对象中,将会以文本形式返回给客户端(页面)

 

 代码块

自定义认证配置,实现WebSecurityConfigurerAdapter 

package com.travelsky.config;

import com.travelsky.auto.login.BeforeLoginFilter;
import com.travelsky.auto.login.LoginProvider;
import com.travelsky.auto.login.OnFailureLogin;
import com.travelsky.auto.token.OnFailureToken;
import com.travelsky.auto.token.TokenFilter;
import com.travelsky.auto.token.TokenProvider;
import org.springframework.beans.factory.annotation.Autowired;
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.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

/**
 * 认证配置
 */
@Configuration
public class WebSecurityConfigurer  extends WebSecurityConfigurerAdapter {

    /**
     * 认证成功处理
     */
    @Autowired
    private SimpleUrlAuthenticationSuccessHandler successHandler;

    /**
     * 登录认证失败后处理
     */
    @Autowired
    private OnFailureLogin failureHandler;

    /**
     * token认证失败后处理
     */
    @Autowired
    private OnFailureToken onFailureToken;

    /**
     * 登录认证逻辑
     */
    @Autowired
    private LoginProvider loginProvider;

    /**
     * token认证逻辑
     */
    @Autowired
    private TokenProvider tokenProvider;

    /**
     * 配置请求权限
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                // 允许登录路径通过
                     .antMatchers("/login").permitAll()
                // 允许业务路径通过
                     .antMatchers("/**").permitAll()
                .and()
                .authorizeRequests()
                .anyRequest()
                .authenticated()
                .and()
                .csrf().disable()
                // 添加拦截器
                     .addFilterBefore(getLoginFilter(), UsernamePasswordAuthenticationFilter.class)
                .addFilterBefore(getTokenFilter(), UsernamePasswordAuthenticationFilter.class);
    }

    /**
     * 登录拦截器
     * @return
     * @throws Exception
     */
    private AbstractAuthenticationProcessingFilter getLoginFilter() throws Exception {
        BeforeLoginFilter beforeLoginFilter = new BeforeLoginFilter("/login", successHandler, failureHandler);
        beforeLoginFilter.setAuthenticationManager(super.authenticationManager());
        return beforeLoginFilter;
    }

    /**
     * token拦截器
     * @return
     * @throws Exception
     */
    private AbstractAuthenticationProcessingFilter getTokenFilter() throws Exception {
        TokenFilter tokenFilter = new TokenFilter("/**", onFailureToken);
        tokenFilter.setAuthenticationManager(super.authenticationManager());
        return tokenFilter;
    }

    /**
     * 配置认证逻辑列表,按顺序进行认证
     * @param auth
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) {
        auth.authenticationProvider(loginProvider);
        auth.authenticationProvider(tokenProvider);
    }
}

 

 

自定义拦截器,继承AbstractAuthenticationProcessingFilter

package com.travelsky.auto.login;

import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;

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

/**
 * 登录自定义拦截器,继承AbstractAuthenticationProcessingFilter
 */
@Slf4j
public class BeforeLoginFilter extends AbstractAuthenticationProcessingFilter {

    /**
     *认证成功的处理对象
     */
    private AuthenticationSuccessHandler successHandler;

    /**
     * 认证失败的处理对象
     */
    private AuthenticationFailureHandler failureHandler;

    public BeforeLoginFilter(String defaultFilterProcessesUrl, AuthenticationSuccessHandler successHandler, AuthenticationFailureHandler failureHandler) {
        super(defaultFilterProcessesUrl);
        this.successHandler = successHandler;
        this.failureHandler = failureHandler;
    }

    /**
     * 过滤器处理逻辑
     * @param request
     * @param response
     * @return
     * @throws AuthenticationException
     * @throws IOException
     * @throws ServletException
     */
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
        log.info("进入登录过滤器");
        String userName = request.getParameter("userName");
        String password = request.getParameter("password");
        LoginInfo loginInfo = new LoginInfo(null, userName, password);
        log.info("username = {}", userName);
        log.info("password = {}", password);

        // 返回指定的认证对象
           LoginAuthenticationToken loginAuthentication = new LoginAuthenticationToken(null, loginInfo, null);

        // 将来由认证逻辑AuthenticationProvider来处理,这里返回LoginAuthenticationToken对象,将来由AuthenticationProvider的supports方法相匹配的认证逻辑来处理
           return this.getAuthenticationManager().authenticate(loginAuthentication);
    }

    /**
     * 认证逻辑认证成功成功后会调用此方法
     * @param request
     * @param response
     * @param chain
     * @param authResult
     * @throws IOException
     * @throws ServletException
     */
    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
        successHandler.onAuthenticationSuccess(request, response, authResult);
    }

    /**
     * 认证逻辑认证失败后调用此方法
     * @param request
     * @param response
     * @param failed
     * @throws IOException
     * @throws ServletException
     */
    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
        failureHandler.onAuthenticationFailure(request, response, failed);
    }
}

 

自定义认证逻辑,继承AuthenticationProvider

package com.travelsky.auto.login;

import com.alibaba.fastjson.JSONObject;
import com.travelsky.pojo.MenuVO;
import com.travelsky.pojo.SysUserInfo;
import com.travelsky.service.SysUserInfoService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.stereotype.Component;

import java.util.List;

/**
 * 认证逻辑
 */
@Component
@Slf4j
public class LoginProvider implements AuthenticationProvider {

    @Autowired
    private SysUserInfoService userInfoService;


    /**
     * 认证逻辑,认证的目标对象由supports断定,supports断定的目标对象是LoginAuthenticationToken自定义认证对象
     * @param authentication
     * @return
     * @throws AuthenticationException
     */
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        log.info("登录验证器,接收参数:{}", JSONObject.toJSONString(authentication));

        // Authentication的具体子类,可由supports断定,supports断定子类是LoginAuthenticationToken,因此可以强转
           final LoginAuthenticationToken loginAuthentication = (LoginAuthenticationToken) authentication;
        final LoginInfo loginInfo = loginAuthentication.getLoginInfo();
        final String userName = loginInfo.getUserName();
        final SysUserInfo userInfo = userInfoService.getByUserId(userName);
        if (userInfo.getPassword().equals(loginInfo.getPassword())) {
            final List<MenuVO> userInfoMenus = userInfoService.getUserInfoMenus(userInfo.getId());
            return new LoginAuthenticationToken(null, loginInfo, userInfoMenus);
        } else {
            log.error("登录验证失败");
            throw new AuthenticationServiceException("登录失败.........");
        }
    }

    /**
     * 指定对谁进行认证,这里指定对LoginAuthenticationToken自定义认证对象进行认证
     * @param authentication
     * @return
     */
    @Override
    public boolean supports(Class<?> authentication) {
        return LoginAuthenticationToken.class.isAssignableFrom(authentication);
    }
}

 

自定义认证成功的处理逻辑,继承SimpleUrlAuthenticationSuccessHandler

package com.travelsky.auto.login;

import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.travelsky.auto.token.TokenContent;
import com.travelsky.auto.token.TokenFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
import org.springframework.stereotype.Component;

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

/**
 * 自定义认证成功处理逻辑,继承SimpleUrlAuthenticationSuccessHandler类
 */
@Component
@Slf4j
public class OnSuccessfulLogin extends SimpleUrlAuthenticationSuccessHandler {

    /**
     * 返回页面的对象,可将对象转化为文本形式(json格式)
     */
    @Autowired
    private ObjectMapper objectMapper;

    /**
     * token工厂
     */
    @Autowired
    private TokenFactory tokenFactory;

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        log.info("登录认证成功,传入参数:{}", JSONObject.toJSONString(authentication));
        // 配置响应编码格式为UTF-8
           response.setCharacterEncoding("UTF-8");
        // 配置返回格式为json
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);

        // Authentication的具体子类为LoginAuthenticationToken
           final LoginAuthenticationToken loginAuthenticationToken = (LoginAuthenticationToken) authentication;
        final TokenContent tokenContent = tokenFactory.createToken("token");
        JSONObject json = new JSONObject();
        json.put("code", "000000");
        json.put("message", "认证成功");
        json.put("claims", tokenContent.getClaims());
        json.put("token", tokenContent.getToken());
        json.put("menuList", loginAuthenticationToken.getUserInfoMenus());
        log.info("生产token:{}",tokenContent.getToken());

        // 将数据保存到objectMapper中,将会转化为文本格式,返回给页面
        objectMapper.writeValue(response.getWriter(), json);
    }
}

 

自定义认证失败逻辑,继承SimpleUrlAuthenticationFailureHandler

package com.travelsky.auto.login;

import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.security.core.AuthenticationException;
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;

@Slf4j
@Component
public class OnFailureLogin extends SimpleUrlAuthenticationFailureHandler {

    @Autowired
    private ObjectMapper objectMapper;

    @Override

    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
        log.info("登录认证失败,传入参数:{}", JSONObject.toJSONString(exception));
        response.setCharacterEncoding("UTF-8");
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        JSONObject json = new JSONObject();
        json.put("code", "999999");
        json.put("message", exception.getMessage());
        objectMapper.writeValue(response.getWriter(), json);
    }
}

 

 

 

自定义认证对象,继承AbstractAuthenticationToken

package com.travelsky.auto.login;

import com.travelsky.pojo.MenuVO;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;

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

/**
 * 自定义登录认证对象
 */
public class LoginAuthenticationToken extends AbstractAuthenticationToken {

    /**
     * 登录数据对象
     */
    private LoginInfo loginInfo;

    /**
     * 菜单数据对象
     */
    private List<MenuVO> userInfoMenus;

    LoginAuthenticationToken(Collection<? extends GrantedAuthority> authorities, LoginInfo loginInfo, List<MenuVO> userInfoMenus) {
        super(authorities);
        this.userInfoMenus = userInfoMenus;
        this.loginInfo = loginInfo;
    }

    @Override
    public Object getCredentials() {
        return loginInfo.getPassword();
    }

    @Override
    public Object getPrincipal() {
        return loginInfo.getUserName();
    }

    public LoginInfo getLoginInfo() {
        return loginInfo;
    }

    public List<MenuVO> getUserInfoMenus() {
        return userInfoMenus;
    }
}

 

自定义拦截器:继承AbstractAuthenticationProcessingFilter,重写attemptAuthentication(HttpServletRequest request, HttpServletResponse response) 方法,返回AbstractAuthenticationToken的子类,AbstractAuthenticationToken的子类可自己定义内容(例如:用户信息,token字符串,Claims),只要继承AbstractAuthenticationToken即可

自定义验证规则:实现AuthenticationProvider接口,authenticate方法参数Authentication与拦截器attemptAuthentication方法返回的是同一个对象,重写 authenticate(Authentication authentication),其中supports(Class<?> authentication)方法用来指定当前检验规则是针对哪个AbstractAuthenticationToken子类对象。springboot自带默认的登录验证是返回UsernamePasswordAuthenticationToken对象。

posted @ 2019-03-21 11:32  antlord  阅读(2117)  评论(0编辑  收藏  举报