HTTP Basic Authentication基本认证机制
HTTP为认证提供了一种原生工具。
尽管我们可以在HTTP的认证形式和cookie的基础上运行自己的认证工具,但在很多情况下,HTTP的原生认证功能就可以很好的满足要求
HTTP的质询/响应认证框架
简化的质询/响应认证框架:
分为四步-请求、质询、认证、成功
认证协议与首部
认证协议是在HTTP认证首部中指定的。
请求:第一条请求没有认证信息
GET /family/jeff.jpg HTTP/1.1
质询:(服务器端的质询)首部是WWW-Authenticate服务器用401状态拒绝了请求,说明需要用户提供用户名和密码。认证算法是在WWW-Authenticate首部中指定的。
HTTP/1.1 401 Authorization Required
WWW-Authentication: Basic realm=”Family”
授权:首部是Authorization 客户端重新发送请求,但这一次会附加一个Authorization首部,用来说明认证算法,用户名和密码。
GET /family/jeff.jpg HTTP/1.1
Authorization: Basic YnssdjJlsdsdsdfsfsdfI
成功:首部是Authentication-Info 如果授权证书是正确的,服务器就会将200 OK文档返回。有些授权算法会在可选的Authentication-Info首部返回一些与授权会话相关的附加信息。
HTTP/1.0 200 OK
Content-Type:image/jpeg
Authorization - 授权
Authentication - 认证
以上摘自网友,下面介绍在工作中遇到的一个需求,项目中需要提供支持Basic Auth的实现。
在Spring中,配置和使用Spring Security,自定义验证过程中的主要实现接口(用户信息UserDetails,用户信息获取服务UserDetailsService,验证工具AuthenticationProvider)即可。其余的流程将由Spring自动接管,非常方便。
UserDetailsService提供了获取UserDetails的方式,只要实现UserDetailsService接口即可,最终生成用户和权限共同组成的UserDetails,在这里就可以实现从自定义的数据源
中获取用户信息了:
public class MyUserDetailsService implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
System.out.println(username);
return new UserDetails() {
@Override
public Collection extends GrantedAuthority> getAuthorities() {
return Arrays.asList(new SimpleGrantedAuthority("ROLE_DBA"));
}
@Override
public String getPassword() {
return "123456";
}
@Override
public String getUsername() {
return "denghb";
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
};
}
}
AuthenticationProvider 提供用户UserDetails的具体验证方式,在这里可以自定义用户密码、验证方式等等。
public class DelegateAuthenticationProvider implements AuthenticationProvider {
private MyUserDetailsService myUserDetailsService = new MyUserDetailsService();
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String username = authentication.getName();
String password = (String) authentication.getCredentials();
//get remote ip address
String remoteAddress = null;
Object details = authentication.getDetails();
if (details instanceof WebAuthenticationDetails) {
WebAuthenticationDetails webDetails = (WebAuthenticationDetails) details;
remoteAddress = webDetails.getRemoteAddress();
}
if (remoteAddress == null || remoteAddress.trim().length() == 0) {
throw new BadCredentialsException("spring security not Authorized Ip");
}
UserDetails userDetails = myUserDetailsService.loadUserByUsername(username);
if(userDetails == null){
throw new BadCredentialsException("Username not found.");
}
Collection extends GrantedAuthority> authorities = userDetails.getAuthorities();
return new UsernamePasswordAuthenticationToken(username, password, Arrays
.asList(new SimpleGrantedAuthority("ROLE_DBA")));
}
@Override
public boolean supports(Class> authentication) {
return true;
}
}
spring security启动配置:
@Configuration
@Order(1)
public class HttpBasicSecurityConfiguration extends WebSecurityConfigurerAdapter {
@Autowired
private DelegateAuthenticationProvider delegateAuthenticationProvider;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authenticationProvider(delegateAuthenticationProvider)
.authorizeRequests()
.antMatchers("/api/**").hasRole("techops")
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and().csrf().disable()
.httpBasic();
}
}
URL拦截配置:
URL拦截配置可以在上述的HttpBasicSecurityConfiguration 中配置,但是此方法适用于大方向上的配置,具体的特殊路径也可以在@Controller的注解中具体配置。
@RequestMapping(value = "/role", method = RequestMethod.GET)
@PreAuthorize("hasAnyRole('ROLE_SUPER_ADMIN')")
@ResponseBody
public String homePageRole() {
return "This my home page";
}
spring boot 启动类:
@EnableAutoConfiguration
@Configuration
@ComponentScan(basePackageClasses={ApplicationStarter.class})
@PropertySource(value = "classpath:/config/application.yml")
@ImportResource({"classpath:spring-context.xml"})
public class ApplicationStarter {
public static void main(String[] args) {
SpringApplication.run(ApplicationStarter.class, args);
}
@Value("${domainCode}")
private String domainCode;
@Bean
public DomainDefine domainDefine() {
DomainDefine domainDefine = new DomainDefine();
domainDefine.setDomainCode(domainCode);
domainDefine.setUserInfoClass("com.dianrong.uniauth.ssclient.bean.SSClientUserExtInfo");
domainDefine.setRejectPublicInvocations(false);
domainDefine.setCustomizedLoginRedirecUrl("/content");
return domainDefine;
}
@Bean
public DelegateAuthenticationProvider delegateAuthenticationProvider() {
return new DelegateAuthenticationProvider()
/*.setDomainDefine(domainCode)*/;
}
}
结果展示:例如访问:http://localhost:8180/api
这种方式去访问:就需要有"techops"权限。第一次进入页面,会有弹出框提示输入用户名和密码。
输入正确的用户名和密码之后,通过谷歌控制台可以看到请求头部有加密后的用户名和密码。
但是我们没有是"techops"的权限,就会看到下面403页面,即没有权限进入。