springSecurity三个入门示例

spring-boot用spring-sercurity很简单,只需要在maven引入相关依赖就好.

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-security</artifactId>
</dependency>

(一)简单示例

public class MyPasswordEncoder implements PasswordEncoder {
    @Override
    public String encode(CharSequence charSequence) {
        return charSequence.toString();
    }

    @Override
    public boolean matches(CharSequence charSequence, String s) {
        return s.equals(charSequence.toString());
    }
}

密码加密类.provider验证的时候回用到.

@Component
public class AppAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
    private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
    @Override
    protected void handle(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        System.out.println("开始根据权限判断将要跳转的页面................");
        String url=determineTargetUrl(authentication);
        redirectStrategy.sendRedirect(request,response,url);
    }
    protected String determineTargetUrl(Authentication authentication){
        String url="";
        Collection<? extends GrantedAuthority> grantedAuthorities = authentication.getAuthorities();
        List<String> roles=new ArrayList<>();
        for(GrantedAuthority grantedAuthority:grantedAuthorities){
            roles.add(grantedAuthority.getAuthority());
        }
        //这里返回路径重定向,同样返回的是controller请求路径,并不是指静态文件的路径
        if(roles.contains("ROLE_ADMIN")){
            return  "/admin";
        }else if(roles.contains("ROLE_DBA")){
            return  "/dba";
        }else if (roles.contains("ROLE_USER")){
            return "/home";
        }else {
            return  "/accessDenied";
        }
    }
}
验证成功跳转处理类.在验证成功后filter调用执行.
@Configuration
public class AppSecurityConfigurer extends WebSecurityConfigurerAdapter {

    @Autowired
    private AuthenticationSuccessHandler authenticationSuccessHandler;
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        System.out.println("开始认证角色..............");
        //这里设置的角色 系统会自动加上role_前缀 既ROLE_ADMIN
        auth.inMemoryAuthentication().passwordEncoder(new MyPasswordEncoder()).withUser("huangLei").password("a7633050").roles("ADMIN","DBA","USE");
        auth.inMemoryAuthentication().passwordEncoder(new MyPasswordEncoder()).withUser("renling").password("huangl").roles("USER");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        System.out.println("开始配置权限...............3");
        //过滤静态登录页面和静态资源,这里的/路径都是指的是controller请求路径并不是指静态页面路径
        http.authorizeRequests()
                .antMatchers("/login","/js/**").permitAll()
                //配置各个访问url的权限
        .antMatchers("/","/home").hasRole("USER")
        .antMatchers("/admin/**").hasAnyRole("ADMIN","DBA")
        .antMatchers("/dba/**").hasAnyRole("DBA")
                //其他页面为需要登录之后
        .anyRequest().authenticated()
                //关闭防御跨站伪造请求,在使用thymeleaf中使用form表单发起请求,如果没有使用th:action则不会报错误,而使用action则会报错,关闭则都不会报错
        .and().csrf().disable()
        .formLogin()
                //登录节点配置
        .loginPage("/login")
                //登录成功后负责跳转设置重定向路径的配置
        .successHandler(authenticationSuccessHandler)
                //失败后跳转的页面
                .failureForwardUrl("/myError")
                //配置登录参数
        .usernameParameter("loginname").passwordParameter("password")
        .and()
        .logout().permitAll()
        .and()
                //配置由权限不足等引起的拒绝跳转的url
        .exceptionHandling().accessDeniedPage("/accessDenied");
    }
}
security最为重要的配置类.包括页面权限以及验证节点等的配置.各个配置的意义都有注释这里不再多说,需要强调的时在使用auth.inMemoryAuthentication()在内存中保存用户信息的时候系统会自动在权限前加上ROLE_前缀.
@Controller
public class SecurityController {
    @RequestMapping("/")
    public String index(ModelAndView modelAndView){

        return "index";
    }
    @RequestMapping("/login")
    public String login(ModelAndView modelAndView){
        System.out.println("login");
        return "login";
    }
    @RequestMapping("/ipLogin")
    public String ipLogin(ModelAndView modelAndView){
        System.out.println("ipLogin");
        return "ipLogin";
    }
    @RequestMapping("/myError")
    public String myError(ModelAndView modelAndView){
        System.out.println("myError");
        return "myError";
    }
    @RequestMapping("/admin")
    public String admin(ModelAndView modelAndView){
        System.out.println("admin");
        modelAndView.addObject("user",getUserName());
        modelAndView.addObject("roles",getAuthority());
        System.out.println(getUserName());
        return "admin";
    }
    @RequestMapping("/dba")
    public String dba(ModelAndView modelAndView){
        System.out.println("dba");
        modelAndView.addObject("user",getUserName());
        modelAndView.addObject("roles",getAuthority());
        System.out.println(getUserName());
        return "dba";
    }
    @RequestMapping("/home")
    public String home(Model model){
        //这里用modelAndView必须要给视图,不然前台无法获取model里的值
        System.out.println("home");
        model.addAttribute("user",getUserName());
        model.addAttribute("role",getAuthority());
        System.out.println(getUserName());
        return "home";
    }
    @RequestMapping("/accessDenied")
    public String accessDenied(ModelAndView modelAndView){
        System.out.println("accessDenied");
        modelAndView.addObject("user",getUserName());
        modelAndView.addObject("roles",getAuthority());
        return "accessDenied";
    }
    @RequestMapping("/logout")
    public String logout(ModelAndView modelAndView, HttpServletRequest request, HttpServletResponse response){
        System.out.println("logout");
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if(authentication!=null){
            new SecurityContextLogoutHandler().logout(request,response,authentication);
        }
        return "redirect:login";
    }
    public String getUserName(){
        //通过获取securityContextHolder获取保存了用户信息的authentication实例对象
        String userName= SecurityContextHolder.getContext().getAuthentication().getName();
        return  userName;
    }
    public String getAuthority(){
        //获取权限
        Collection<? extends GrantedAuthority> grantedAuthorities = SecurityContextHolder.getContext().getAuthentication().getAuthorities();
        ArrayList<String> pers = new ArrayList<>();
        for (GrantedAuthority grantedAuthority:grantedAuthorities){
            pers.add(grantedAuthority.getAuthority());
        }
        return  pers.toString();
    }
}

页面跳转控制器,这里没有太多逻辑,仅仅是负责页面跳转.

(二)企业开发中常见的使用示例

@Service
public class SystemUserSerivce implements UserDetailsService {
    @Autowired
    private PermissionMapper permissionMapper;
    @Autowired
    private UserMapper userMapper;
    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
        User myUser =userMapper.selectUserByUsername(s);
        List<Permission> permissions = permissionMapper.selectPermissionsByUserName(s);
        List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
        for(Permission permission:permissions){
            String permissionCode = permission.getPermissionCode();
            GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(permissionCode);
            grantedAuthorities.add(grantedAuthority);
        }
        //这里provider需要的是springSecurity的user对象
        return new org.springframework.security.core.userdetails.User(s,myUser.getPassword(),grantedAuthorities);
    }
}

userDetailService是被daoAuthenticationProvider调用用来加载用户及权限的接口,这里需要覆盖loadUserByUsername来从数据库加载用户信息.

@Configuration
public class AppSecurityConfigurer extends WebSecurityConfigurerAdapter {
    @Autowired
    private SystemUserSerivce systemUserSerivce;
    @Autowired
    private PasswordEncoder passwordEncoder;
    @Autowired
    private AuthenticationProvider authenticationProvider ;
    @Autowired
    private AuthenticationSuccessHandler authenticationSuccessHandler;


    @Bean
    public PasswordEncoder passwordEncoder(){
        //推荐使用该密码加密类
        return  new BCryptPasswordEncoder();
    }
    @Bean
    public  AuthenticationProvider authenticationProvider(){
        DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
        //是否隐藏没有找到用户的异常
        daoAuthenticationProvider.setHideUserNotFoundExceptions(false);
        //设置userService实例
        daoAuthenticationProvider.setUserDetailsService(systemUserSerivce);
        //设置密码加密类
        daoAuthenticationProvider.setPasswordEncoder(passwordEncoder);
        return  daoAuthenticationProvider;
    }


    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //注册daoAutheticationProvider
        auth.authenticationProvider(authenticationProvider);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        System.out.println("开始配置权限...............4");
        //过滤静态登录页面和静态资源,这里的/路径都是指的是controller请求路径并不是指静态页面路径
        http.authorizeRequests()
                .antMatchers("/login","/js/**").permitAll()
        .antMatchers("/","/home").hasRole("USER")
        .antMatchers("/admin/**").hasAnyRole("ADMIN","DBA")
        .antMatchers("/dba/**").hasAnyRole("DBA")
        .anyRequest().authenticated()
        .and().csrf().disable()
        .formLogin()
        .loginPage("/login")
        .successHandler(authenticationSuccessHandler)
                .failureForwardUrl("/myError")
        .usernameParameter("loginname").passwordParameter("password")
        .and()
        .logout().permitAll()
        .and()
        .exceptionHandling().accessDeniedPage("/accessDenied");
    }
}

另外,控制器类与successHandler与第一个示例相同,这里不做赘述.

这里与第一个示例不同的地方是我们需要从数据库中加载用户信息,所以我们选择实用daoAuthenticationProvider这个类,然后我们写一个类来继承负责加载数据库user信息的userDetailService的loadUserByUsername这个方法.这个示例是我们日常开发过程中实用的比较多的方式,在示例后面将会联系源码做重点分析

(三)依据ip作为登录验证参数的示例

这里是原文链接:

https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&mid=2247483980&idx=1&sn=cb40ba4fea5cf100a98896d9a0404a43&chksm=fa497dfdcd3ef4ebdd162db2f674d882fd87d2648c775272a8af238c0500289d439d858804e5&scene=21#wechat_redirect

public class IpAuthenticationToken extends AbstractAuthenticationToken {
    /**
     *  对比UsernamePasswordAuthenticationToken 中的账号principal 密码credentials
     */
    private  String ip;

    public String getIp() {
        return ip;
    }

    public void setIp(String ip) {
        this.ip = ip;
    }

    public  IpAuthenticationToken(String ip){
        super(null);
        this.ip=ip;
        //认证前
        super.setAuthenticated(false);
    }
    public  IpAuthenticationToken(String ip, Collection< ? extends GrantedAuthority> authorities){
        super(authorities);
        this.ip=ip;
        //认证后
        super.setAuthenticated(true);
    }
    @Override
    public Object getCredentials() {
        return null;
    }

    @Override
    public Object getPrincipal() {
        return this.ip;
    }
}

封装了用户ip的token类

public class IpAuthenticationProvider implements AuthenticationProvider {
    final static Map<String , SimpleGrantedAuthority> ipAuthorityMap=new ConcurrentHashMap<>();
    //维护一个权限Map
    static {
        ipAuthorityMap.put("127.0.0.1",new SimpleGrantedAuthority("ROLE_ADMIN"));
        ipAuthorityMap.put("10.236.69.103",new SimpleGrantedAuthority("ROLE_USER"));
        ipAuthorityMap.put("127.0.0.5",new SimpleGrantedAuthority("ROLE_DBA"));
    }
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        //IpAuthenticationToken 是authentication的实现类
        IpAuthenticationToken ipAuthenticationToken = (IpAuthenticationToken) authentication;
        String ip = ipAuthenticationToken.getIp();
        SimpleGrantedAuthority simpleGrantedAuthority = ipAuthorityMap.get(ip);
        if (simpleGrantedAuthority!=null){
          return  new IpAuthenticationToken(ip, Arrays.asList(simpleGrantedAuthority));
        }
        return null;
    }

    @Override
    public boolean supports(Class<?> authentication) {
        //只支持IPAuthentication进行认证
        return (IpAuthenticationToken.class.isAssignableFrom(authentication));
    }
}

负责ip验证的provider

public class IpAuthenticationProcessingFilter extends AbstractAuthenticationProcessingFilter {
     IpAuthenticationProcessingFilter(){
        super(new AntPathRequestMatcher("/ipVerify"));
    }

    /**
     *  在AbstractAuthenticationProcessingFilter的DoFilter方法中被调用
     * @param request
     * @param response
     * @return
     * @throws AuthenticationException
     * @throws IOException
     * @throws ServletException
     */
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
         String hostIp=request.getRemoteHost();
         return getAuthenticationManager().authenticate(new IpAuthenticationToken(hostIp));
    }
}

负责sercurity验证的过滤器.

@Configuration
public class AppSecurityConfiger extends WebSecurityConfigurerAdapter {
    @Autowired
    private  IpAuthenticationProvider ipAuthenticationProvider;
    @Autowired
    private AppAuthenticationSuccessHandler appAuthenticationSuccessHandler;
    @Bean
    public  IpAuthenticationProvider ipAuthenticationProvider(){
        return  new IpAuthenticationProvider();
    }

    /**
     * 这里会覆盖configure中的SuccessHandler和异常的配置
     * @param authenticationManager
     * @return
     */
    IpAuthenticationProcessingFilter ipAuthenticationProcessingFilter(AuthenticationManager authenticationManager){
        IpAuthenticationProcessingFilter ipAuthenticationProcessingFilter = new IpAuthenticationProcessingFilter();
        //添加认证管理器
        ipAuthenticationProcessingFilter.setAuthenticationManager(authenticationManager);
        //添加成功处理器
        ipAuthenticationProcessingFilter.setAuthenticationSuccessHandler(appAuthenticationSuccessHandler);
        //添加失败处理器
        ipAuthenticationProcessingFilter.setAuthenticationFailureHandler(new SimpleUrlAuthenticationFailureHandler("/myError"));
        return  ipAuthenticationProcessingFilter;
    }
  

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(ipAuthenticationProvider);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        System.out.println("开始配置权限...............5");
        //过滤静态登录页面和静态资源,这里的/路径都是指的是controller请求路径并不是指静态页面路径
        http.authorizeRequests()
                .antMatchers("/ipLogin","/js/**").permitAll()
                .antMatchers("/","/home").hasRole("USER")
                .antMatchers("/admin/**").hasAnyRole("ADMIN","DBA")
                .antMatchers("/dba/**").hasAnyRole("DBA")
                .anyRequest().authenticated()
                .and().csrf().disable()
                .formLogin()
                .loginPage("/ipLogin")
              //  .successHandler(appAuthenticationSuccessHandler)
              //  .failureForwardUrl("/myError")
                .and()
                .logout().permitAll()
                .and()
                .exceptionHandling().accessDeniedPage("/accessDenied");
        //这里的得注意插入过滤器的位置
        http.addFilterBefore(ipAuthenticationProcessingFilter(authenticationManager()), UsernamePasswordAuthenticationFilter.class);
    }
}

这里我舍去了原文中LoginUrlAuthenticationEntryPoint的配置,选择直接在configure中配置验证拦截点的配置,另外发现新增的ipFilter的处理跳转的successHandler和failureForwardUrl无法在configure中配置,会失效

(四)以UsernamePasswordAuthenticationFilter为例分析sercurity的核心流程

WebSecurityConfigurerAdapter会获取到sercurity相关的filterChain(这部分源码没找到),然后挨个执行filter中的doFilter方法,每个filter都会继承抽象类AbstractAuthenticationProcessingFilter.
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
        throws IOException, ServletException {

    HttpServletRequest request = (HttpServletRequest) req;
    HttpServletResponse response = (HttpServletResponse) res;
    if (!requiresAuthentication(request, response)) {
        chain.doFilter(request, response);
        return;
    }
    if (logger.isDebugEnabled()) {
        logger.debug("Request is to process authentication");
    }
    Authentication authResult;
    try {
        //在UsernamePasswordAutheticationFilter中被覆盖
        authResult = attemptAuthentication(request, response);
        if (authResult == null) {
            // return immediately as subclass has indicated that it hasn't completed
            // authentication
            return;
        }
        sessionStrategy.onAuthentication(authResult, request, response);
    }
    catch (InternalAuthenticationServiceException failed) {
        logger.error(
                "An internal error occurred while trying to authenticate the user.",
                failed);
        unsuccessfulAuthentication(request, response, failed);

        return;
    }
    catch (AuthenticationException failed) {
        // Authentication failed
        unsuccessfulAuthentication(request, response, failed);

        return;
    }

    // Authentication success
    if (continueChainBeforeSuccessfulAuthentication) {
        chain.doFilter(request, response);
    }
    //调用seccessHandler进行调整控制
    successfulAuthentication(request, response, chain, authResult);
}

usernamePasswordAuthenticationFilter:

public Authentication attemptAuthentication(HttpServletRequest request,
      HttpServletResponse response) throws AuthenticationException {
   if (postOnly && !request.getMethod().equals("POST")) {
      throw new AuthenticationServiceException(
            "Authentication method not supported: " + request.getMethod());
   }

   String username = obtainUsername(request);
   String password = obtainPassword(request);

   if (username == null) {
      username = "";
   }

   if (password == null) {
      password = "";
   }

   username = username.trim();

   UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
         username, password);

   // Allow subclasses to set the "details" property
   setDetails(request, authRequest);

   return this.getAuthenticationManager().authenticate(authRequest);
}

providerManager:遍历各个provider进行验证

public Authentication authenticate(Authentication authentication)
      throws AuthenticationException {
   Class<? extends Authentication> toTest = authentication.getClass();
   AuthenticationException lastException = null;
   Authentication result = null;
   boolean debug = logger.isDebugEnabled();

   for (AuthenticationProvider provider : getProviders()) {
      if (!provider.supports(toTest)) {
         continue;
      }

      if (debug) {
         logger.debug("Authentication attempt using "
               + provider.getClass().getName());
      }

      try {
         result = provider.authenticate(authentication);

         if (result != null) {
            copyDetails(authentication, result);
            break;
         }
      }
      catch (AccountStatusException e) {
         prepareException(e, authentication);
         // SEC-546: Avoid polling additional providers if auth failure is due to
         // invalid account status
         throw e;
      }
      catch (InternalAuthenticationServiceException e) {
         prepareException(e, authentication);
         throw e;
      }
      catch (AuthenticationException e) {
         lastException = e;
      }
   }

   if (result == null && parent != null) {
      // Allow the parent to try.
      try {
         result = parent.authenticate(authentication);
      }
      catch (ProviderNotFoundException e) {
         // ignore as we will throw below if no other exception occurred prior to
         // calling parent and the parent
         // may throw ProviderNotFound even though a provider in the child already
         // handled the request
      }
      catch (AuthenticationException e) {
         lastException = e;
      }
   }

   if (result != null) {
      if (eraseCredentialsAfterAuthentication
            && (result instanceof CredentialsContainer)) {
         // Authentication is complete. Remove credentials and other secret data
         // from authentication
         ((CredentialsContainer) result).eraseCredentials();
      }

      eventPublisher.publishAuthenticationSuccess(result);
      return result;
   }

   // Parent was null, or didn't authenticate (or throw an exception).

   if (lastException == null) {
      lastException = new ProviderNotFoundException(messages.getMessage(
            "ProviderManager.providerNotFound",
            new Object[] { toTest.getName() },
            "No AuthenticationProvider found for {0}"));
   }

   prepareException(lastException, authentication);

   throw lastException;
}

abstractUserDetailsAuthenticationProvider:调用userDetailService加载数据库里的user数据,并调用daoAuthenticationProvider的

additionalAuthenticationChecks进行账号密码匹配验证.

public Authentication authenticate(Authentication authentication)
      throws AuthenticationException {
   Assert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication,
         messages.getMessage(
               "AbstractUserDetailsAuthenticationProvider.onlySupports",
               "Only UsernamePasswordAuthenticationToken is supported"));

   // Determine username
   String username = (authentication.getPrincipal() == null) ? "NONE_PROVIDED"
         : authentication.getName();

   boolean cacheWasUsed = true;
   UserDetails user = this.userCache.getUserFromCache(username);

   if (user == null) {
      cacheWasUsed = false;

      try {
         user = retrieveUser(username,
               (UsernamePasswordAuthenticationToken) authentication);
      }
      catch (UsernameNotFoundException notFound) {
         logger.debug("User '" + username + "' not found");

         if (hideUserNotFoundExceptions) {
            throw new BadCredentialsException(messages.getMessage(
                  "AbstractUserDetailsAuthenticationProvider.badCredentials",
                  "Bad credentials"));
         }
         else {
            throw notFound;
         }
      }

      Assert.notNull(user,
            "retrieveUser returned null - a violation of the interface contract");
   }

   try {
      preAuthenticationChecks.check(user);
      additionalAuthenticationChecks(user,
            (UsernamePasswordAuthenticationToken) authentication);
   }
   catch (AuthenticationException exception) {
      if (cacheWasUsed) {
         // There was a problem, so try again after checking
         // we're using latest data (i.e. not from the cache)
         cacheWasUsed = false;
         user = retrieveUser(username,
               (UsernamePasswordAuthenticationToken) authentication);
         preAuthenticationChecks.check(user);
         additionalAuthenticationChecks(user,
               (UsernamePasswordAuthenticationToken) authentication);
      }
      else {
         throw exception;
      }
   }

   postAuthenticationChecks.check(user);

   if (!cacheWasUsed) {
      this.userCache.putUserInCache(user);
   }

   Object principalToReturn = user;

   if (forcePrincipalAsString) {
      principalToReturn = user.getUsername();
   }

   return createSuccessAuthentication(principalToReturn, authentication, user);
}

userDetailService加载数据库中国的user信息

@Service
public class SystemUserSerivce implements UserDetailsService {
    @Autowired
    private PermissionMapper permissionMapper;
    @Autowired
    private UserMapper userMapper;
    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
        User myUser =userMapper.selectUserByUsername(s);
        List<Permission> permissions = permissionMapper.selectPermissionsByUserName(s);
        List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
        for(Permission permission:permissions){
            String permissionCode = permission.getPermissionCode();
            GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(permissionCode);
            grantedAuthorities.add(grantedAuthority);
        }
        //这里provider需要的是springSecurity的user对象
        return new org.springframework.security.core.userdetails.User(s,myUser.getPassword(),grantedAuthorities);
    }
}

daoAuthenticationProvider 账号密码匹配

protected void additionalAuthenticationChecks(UserDetails userDetails,
      UsernamePasswordAuthenticationToken authentication)
      throws AuthenticationException {
   if (authentication.getCredentials() == null) {
      logger.debug("Authentication failed: no credentials provided");

      throw new BadCredentialsException(messages.getMessage(
            "AbstractUserDetailsAuthenticationProvider.badCredentials",
            "Bad credentials"));
   }

   String presentedPassword = authentication.getCredentials().toString();

   if (!passwordEncoder.matches(presentedPassword, userDetails.getPassword())) {
      logger.debug("Authentication failed: password does not match stored value");

      throw new BadCredentialsException(messages.getMessage(
            "AbstractUserDetailsAuthenticationProvider.badCredentials",
            "Bad credentials"));
   }
}

如果验证通过则abstractUserDetailsAuthenticationProvider会调用createSuccessAuthentication将权限信息也塞入token,并返回,如果验证不通过继续循环下一个provider

protected Authentication createSuccessAuthentication(Object principal,
      Authentication authentication, UserDetails user) {
   // Ensure we return the original credentials the user supplied,
   // so subsequent attempts are successful even with encoded passwords.
   // Also ensure we return the original getDetails(), so that future
   // authentication events after cache expiry contain the details
   UsernamePasswordAuthenticationToken result = new UsernamePasswordAuthenticationToken(
         principal, authentication.getCredentials(),
         authoritiesMapper.mapAuthorities(user.getAuthorities()));
   result.setDetails(authentication.getDetails());

   return result;

回到filter,验证通过,并且返回了一个新的authentication ,包含了用户信息和权限,successfulAuthentication调用执行successHandler开始根据权限进行跳转

protected void successfulAuthentication(HttpServletRequest request,
      HttpServletResponse response, FilterChain chain, Authentication authResult)
      throws IOException, ServletException {

   if (logger.isDebugEnabled()) {
      logger.debug("Authentication success. Updating SecurityContextHolder to contain: "
            + authResult);
   }

   SecurityContextHolder.getContext().setAuthentication(authResult);

   rememberMeServices.loginSuccess(request, response, authResult);

   // Fire event
   if (this.eventPublisher != null) {
      eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(
            authResult, this.getClass()));
   }

   successHandler.onAuthenticationSuccess(request, response, authResult);
}

这里以simpleUrlAuthenticationSuccessHandler为例:

public void onAuthenticationSuccess(HttpServletRequest request,
      HttpServletResponse response, Authentication authentication)
      throws IOException, ServletException {

   handle(request, response, authentication);
   clearAuthenticationAttributes(request);
}

handler就是我们重写的覆盖的方法

protected void handle(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
    System.out.println("开始根据权限判断将要跳转的页面................");
    String url=determineTargetUrl(authentication);
    redirectStrategy.sendRedirect(request,response,url);
}
protected String determineTargetUrl(Authentication authentication){
    String url="";
    Collection<? extends GrantedAuthority> grantedAuthorities = authentication.getAuthorities();
    List<String> roles=new ArrayList<>();
    for(GrantedAuthority grantedAuthority:grantedAuthorities){
        roles.add(grantedAuthority.getAuthority());
    }
    //这里返回路径重定向,同样返回的是controller请求路径,并不是指静态文件的路径
    if(roles.contains("ROLE_ADMIN")){
        return  "/admin";
    }else if(roles.contains("ROLE_DBA")){
        return  "/dba";
    }else if (roles.contains("ROLE_USER")){
        return "/home";
    }else {
        return  "/accessDenied";
    }
}

 

至此,sercurity的核心流程全部结束.

在最后,补上一个从芋道源码获取的整体的流程图

 
  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值