简介
参考自定义认证逻辑中的Authentication 类
public interface Authentication extends Principal, Serializable {
Collection<? extends GrantedAuthority> getAuthorities();
Object getCredentials();
Object getDetails();
Object getPrincipal();
boolean isAuthenticated();
void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException;
}
其中有一个方法叫做 getDetails。该方法实际上就是用来存储有关身份认证的其他信息的,例如 IP 地址、证书信息
在默认情况下,这里存储的就是用户登录的 IP 地址和 sessionId。
那么我们就可通过该方法获取到相关的IP地址信息
用户登录必经的一个过滤器就是 UsernamePasswordAuthenticationFilter,在该类的 attemptAuthentication 方法中,对请求参数做提取,在 attemptAuthentication 方法中,会调用到一个方法,就是 setDetails。
protected void setDetails(HttpServletRequest request,
UsernamePasswordAuthenticationToken authRequest) {
authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
}
UsernamePasswordAuthenticationToken 是 Authentication 的具体实现,
所以这里实际上就是在设置 details,
至于 details 的值,则是通过 authenticationDetailsSource 来构建的
WebAuthenticationDetailsSource
public class WebAuthenticationDetailsSource implements AuthenticationDetailsSource<HttpServletRequest, WebAuthenticationDetails> {
public WebAuthenticationDetailsSource() {
}
public WebAuthenticationDetails buildDetails(HttpServletRequest context) {
return new WebAuthenticationDetails(context);
}
}
WebAuthenticationDetails
public class WebAuthenticationDetails implements Serializable {
private static final long serialVersionUID = 550L;
private final String remoteAddress;
private final String sessionId;
public WebAuthenticationDetails(HttpServletRequest request) {
this.remoteAddress = request.getRemoteAddr();
HttpSession session = request.getSession(false);
this.sessionId = session != null ? session.getId() : null;
}
private WebAuthenticationDetails(String remoteAddress, String sessionId) {
this.remoteAddress = remoteAddress;
this.sessionId = sessionId;
}
......
}
默认通过 WebAuthenticationDetailsSource 来构建 WebAuthenticationDetails,并将结果设置到 Authentication 的 details 属性中去。而 WebAuthenticationDetails 中定义的属性,大家看一下基本上就明白,这就是保存了用户登录地址和 sessionId。
用户登录的 IP 地址实际上我们可以直接从 WebAuthenticationDetails 中获取到。
则当我们登陆成功后,我们可以在程序的任何地方获取到相对应的details
@Service
public class HelloService {
public void hello() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
WebAuthenticationDetails details = (WebAuthenticationDetails) authentication.getDetails();
System.out.println(details);
}
}
定制
当然,WebAuthenticationDetails 也可以自己定制,
因为默认它只提供了 IP 和 sessionid 两个信息,如果我们想保存关于 Http 请求的更多信息,就可以通过自定义 WebAuthenticationDetails 来实现。
例如验证码验证也可以放在自定义的WebAuthenticationDetails中进行操作
我们定义如下两个类:
MyWebAuthenticationDetails
public class MyWebAuthenticationDetails extends WebAuthenticationDetails {
private static final long serialVersionUID = 603157756847194200L;
private boolean isCaptcha;
public MyWebAuthenticationDetails(HttpServletRequest req) {
super(req);
String code = req.getParameter("code");
String verify_code = (String) req.getSession().getAttribute("verify_code");
if(code != null && code.equals(verify_code)) {
isCaptcha = true;
}
}
public boolean isPassed() {
return isCaptcha;
}
}
MyWebAuthenticationDetailsSource
@Component
public class MyWebAuthenticationDetailsSource extends WebAuthenticationDetailsSource {
@Override
public MyWebAuthenticationDetails buildDetails(HttpServletRequest context) {
return new MyWebAuthenticationDetails(context);
}
}
执行逻辑如下:
- 首先我们定义 MyWebAuthenticationDetails,由于它的构造方法中,刚好就提供了 HttpServletRequest 对象,所以我们可以直接利用该对象进行验证码判断,并将判断结果交给 isPassed 变量保存。
- 「如果我们想扩展属性,只需要在 MyWebAuthenticationDetails 中再去定义更多属性,然后从 HttpServletRequest 中提取出来设置给对应的属性即可,这样,在登录成功后就可以随时随地获取这些属性了。」
- 最后在 MyWebAuthenticationDetailsSource 中构造并返回。
接下来,我们就可以直接在 MyAuthenticationProvider 中进行调用了:
MyAuthenticationProvider
public class MyAuthenticationProvider extends DaoAuthenticationProvider {
@Override
protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
if (!((MyWebAuthenticationDetails) authentication.getDetails()).isCaptcha()) {
throw new AuthenticationServiceException("验证码错误");
}
super.additionalAuthenticationChecks(userDetails, authentication);
}
}
直接从 authentication 中获取到 details 并调用 isCaptcha方法
有问题就抛出异常即可。
配置使用自定义
最后的问题就是如何用自定义的 MyWebAuthenticationDetailsSource 代替系统默认的 WebAuthenticationDetailsSource,很简单,我们只需要在 SecurityConfig 中稍作定义即可:
@Autowired
MyWebAuthenticationDetailsSource myWebAuthenticationDetailsSource;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/captcha")
.permitAll()
.antMatchers("/rememberme/**")
.rememberMe()//该接口只有使用了remember登录才能访问
.antMatchers("/admin/**")
.fullyAuthenticated()//fullyAuthenticated 不同于 authenticated,fullyAuthenticated 不包含自动登录的形式,而
.anyRequest()
.authenticated()
.and() //所有请求都需要认证才能访问
.formLogin()
.authenticationDetailsSource(myWebAuthenticationDetailsSource)
.permitAll()
.successHandler((req, resp, auth) -> {//auth: 当前登录成功的用户信息
resp.setContentType("application/json;charset=utf-8");
PrintWriter writer = resp.getWriter();
writer.write(new ObjectMapper().writeValueAsString(auth.getPrincipal()));
writer.flush();
writer.close();
})
.failureHandler((req, resp, exception) -> {
resp.setContentType("application/json;charset=utf-8");
PrintWriter writer = resp.getWriter();
writer.write(new ObjectMapper().writeValueAsString(exception.getMessage()));
writer.flush();
writer.close();
})
.and()
.rememberMe()
.key("zukxu")
.tokenRepository(jdbcTokenRepository())
.and()//添加记住我功能
.csrf().disable()//关闭csrf
;
}
.formLogin()
.authenticationDetailsSource(myWebAuthenticationDetailsSource)
将 MyWebAuthenticationDetailsSource 注入到 SecurityConfig 中,并在 formLogin 中配置 authenticationDetailsSource 即可成功使用我们自定义的 WebAuthenticationDetails。
这样自定义完成后,WebAuthenticationDetails 中原有的功能依然保留,也就是我们还可以利用老办法继续获取用户 IP 以及 sessionId 等信息,如下:
@Service
public class HelloService {
public void hello() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
MyWebAuthenticationDetails details = (MyWebAuthenticationDetails) authentication.getDetails();
System.out.println(details);
}
}
这里类型强转的时候,转为 MyWebAuthenticationDetails 即可。
测试
MyWebAuthenticationDetails [RemoteIpAddress=0:0:0:0:0:0:0:1, SessionId=D54876F951918A08852C1803FE26DA20] |