个性化登陆认证

自定义表单登录页面

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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;

@Configuration
public class BrowserSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.formLogin()  //表单登陆认证方法,浏览器跳转到specurity默认登陆页面
        //http.httpBasic()//浏览器弹出登陆对话框登陆认证方式
        .loginPage("/login.html")//自定义登陆页面
.loginProcessingUrl("/user/login")//用户登陆表单提交接口,默认/login
        .and()
        .authorizeRequests() ////设置请求符合访问资源的权限
        .antMatchers("/login.html","/error.html")//匹配器
.permitAll()//访问这两个页面不需要认证
        .anyRequest().authenticated(); //对任何请求都要登陆认证后才能访问
    }
    @Bean
    public PasswordEncoder passwordEncoder() {
        //return NoOpPasswordEncoder.getInstance(); //已弃用
        return new BCryptPasswordEncoder();
    }
}

页面放入resources/static

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="/login" method="post">
    用户名: <input type="text" name="username123" /> <br/>
    密码:<input type="password" name="password123" /> <br/>
    <input type="submit" value="登陆"/>
</form>
</body>
</html>

 

表单登陆的接口默认是/login,是由UsernamePasswordAuthenticationFilter过滤器的无参构造函数处理的

框架源码

public UsernamePasswordAuthenticationFilter() {
    super(new AntPathRequestMatcher("/login", "POST"));
}

.loginProcessingUrl("/user/login")//用户登陆表单提交接口

 

改变用户登陆提交接口(不需要定义接口),由UsernamePasswordAuthenticationFilter过滤器的attemptAuthentication方法处理.

实现UsernamePasswordAuthenticationFilter的attemptAuthentication方法可自定义逻辑

框架源码

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 username = this.obtainUsername(request);
        String password = this.obtainPassword(request);
        if (username == null) {
            username = "";
        }

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

        username = username.trim();
        UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
        this.setDetails(request, authRequest);
        return this.getAuthenticationManager().authenticate(authRequest);
    }
}

自定义身份认证跳转

自定义表单登陆页面是一种统一的身份认证方式,有的场景不需要返回这种html的登陆页面,需要返回json,包含状态码和错误信息

1.请求的身份认证由security判断,需要认证跳转到自定义的Controller方法上,这个方法url在配置类configure方法中http.loginPage("/authentication/require")配置(登陆静态页改为url)

2.在跳转到自定义Controller方法之前,security会将这次请求缓存在HttpSeesionRequestCache类中,自定义Controller类可以设置这个类为成员属性拿到这次请求的缓存

3.跳转登陆页面是通过配置选择使用该模块的统一登陆页面,还是使用该模块的模块自定义登陆页面

4.请求url是非.html,则返回json信息

borowser模块

import com.wzl.properties.SecurityProperties;
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.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;

@Configuration
public class BrowserSecurityConfig extends WebSecurityConfigurerAdapter {
    //security配置类
    @Autowired
    private SecurityProperties securityProperties;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.formLogin()  //表单登陆认证方法,浏览器跳转到specurity默认登陆页面
        //http.httpBasic()//浏览器弹出登陆对话框登陆认证方式
        //.loginPage("/login.html")//自定义登陆页面,自定义登陆url,security判断请求是否需要登陆认证,如果需要跳转该登陆页面
        .loginPage("/authentication/require")//自定义登陆url,security判断请求是否需要登陆认证,如果需要跳转该url
        .loginProcessingUrl("/user/login")//用户登陆页面提交接口
        .and()
        .authorizeRequests() ////设置请求符合访问资源的权限
        .antMatchers(securityProperties.getBrowser().getLoginPage(),"/login.html","/error.html","/authentication/require")//匹配器
        .permitAll()//匹配器匹配的请求不需要认证
        .anyRequest().authenticated() //对任何请求都要登陆认证后才能访问
        .and()
        .csrf().disable();//关闭跨域伪造关闭
    }
    @Bean
    public PasswordEncoder passwordEncoder() {
        //return NoOpPasswordEncoder.getInstance(); //已弃用
        return new BCryptPasswordEncoder();
    }
}

 

.loginPage("/authentication/require") 跳转的控制器方法

import com.wzl.properties.SecurityProperties;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.web.DefaultRedirectStrategy;
import org.springframework.security.web.RedirectStrategy;
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
import org.springframework.security.web.savedrequest.RequestCache;
import org.springframework.security.web.savedrequest.SavedRequest;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

@Slf4j
@RestController
public class BrowserSecurityController {
    //security缓存了需要身份认证的请求
    private RequestCache requestCache = new HttpSessionRequestCache();
    //跳转工具
    private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
    //security配置类
    @Autowired
    private SecurityProperties securityProperties;
    /** .loginPage("/authentication/require")
     * 当身份认证时,跳转这里
     * @param request
     * @param response
     * @return
     */
    @GetMapping("/authentication/require")
    @ResponseStatus(code = HttpStatus.UNAUTHORIZED) //401  未授权状态码
    public ResponseEntity<Map<String,Object>> requireAuthentication(HttpServletRequest request, HttpServletResponse response) throws IOException {
        HashMap<String, Object> map = new HashMap<>();
        //从缓存中获取这次请求
        SavedRequest savedRequest = requestCache.getRequest(request, response);
        if (savedRequest!=null) {
            String url = savedRequest.getRedirectUrl();
            log.info("引发跳转的请求:"+url);
            //请求url是.html,跳转到登陆页
            if (StringUtils.endsWithIgnoreCase(url, ".html")) {
                //跳转的页面可以是统一的自定义登陆页面,也可以是使用该模块的模块自定义登陆面
                redirectStrategy.sendRedirect(request,response,securityProperties.getBrowser().getLoginPage());

            }
        }
        //返回需要身份认证的提示信息
        map.put("msg", "访问的服务需要身份认证,请引导用户到登录页");
        return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(map);
    }
}

配置项封装

  1. 不使用统一自定义登陆页面,通过配置当前模块自定义登陆页面
# security配置
wzl:
  security:
    browser:
      loginPage: "/myLogin.html" #当前模块resources/static/

Core模块

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.boot.context.properties.ConfigurationProperties;

@Data
@NoArgsConstructor
@AllArgsConstructor
@ConfigurationProperties(prefix = "wzl.security")
public class SecurityProperties {
    private BrowserProperties browser = new BrowserProperties();
}
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class BrowserProperties {
    private String loginPage = "/login.html";
}
import com.wzl.properties.SecurityProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;

@Configuration
@EnableConfigurationProperties(SecurityProperties.class)
public class SecurityCoreConfig {
}

自定义登录成功处理

登陆成功,用户积分增加等业务。

public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
    private String url;

    public MyAuthenticationSuccessHandler(String url){
        this.url = url;
    }

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        User user = (User) authentication.getPrincipal();
        System.out.println(user.getPassword());//输出null,保护密码
        System.out.println(user.getUsername());
        System.out.println(user.getRoles());
        response.sendRedirect(url);
    }
}

 

//登陆成功,是否指定跳转到首页
//.defaultSuccessUrl("/main.html",true)
//.defaultSuccessUrl("/toMain",true)
.successHandler(new MyAuthenticationSuccessHandler("http://www.baidu.com"))

自定义登陆失败处理
public class MyAuthenticationFailureHandler implements AuthenticationFailureHandler {
    
    private String url;
    
    public MyAuthenticationFailureHandler(String url){
        this.url = url;
    }
    
    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
        response.sendRedirect(url);
    }
}

 

//.failureUrl("/error.html")
.failureHandler(new MyAuthenticationFailureHandler("http://www.baidu.com"))

posted @ 2024-01-17 18:01  wangzhilei  阅读(52)  评论(0)    收藏  举报