学成-认证实现
一、Spring-security
1.集成Spring-security
依赖引入
1 2 3 4 5 6 7 8
| <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-oauth2</artifactId> </dependency>
|
配置类导入
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
|
@EnableWebSecurity @EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled = true) public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Bean public UserDetailsService userDetailsService() { InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager(); manager.createUser(User.withUsername("zhangsan").password("123").authorities("p1").build()); manager.createUser(User.withUsername("lisi").password("456").authorities("p2").build()); return manager; }
@Bean public PasswordEncoder passwordEncoder() {
return NoOpPasswordEncoder.getInstance();
}
@Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers("/r/**").authenticated() .anyRequest().permitAll() .and() .formLogin().successForwardUrl("/login-success"); }
}
|
接口授权
1 2 3 4 5 6 7 8
| @Bean public UserDetailsService userDetailsService() { InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager(); manager.createUser(User.withUsername("zhangsan").password("123").authorities("p1").build()); manager.createUser(User.withUsername("lisi").password("456").authorities("p2").build()); return manager; }
|
接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| @RestController public class LoginController { .... @RequestMapping("/r/r1") @PreAuthorize("hasAuthority('p1')") public String r1(){ return "访问r1资源"; } @RequestMapping("/r/r2") @PreAuthorize("hasAuthority('p2')") public String r2(){ return "访问r2资源"; } ...
|
访问/r/r1,使用zhangsan登录可以正常访问,因为在/r/r1的方法上指定了权限p1,zhangsan用户拥有权限p1,所以可以正常访问
访问/r/r1,使用lisi登录则拒绝访问,由于lisi用户不具有权限p1需要拒绝访问
2.OAuth2授权
OAuth2(开放授权2.0)是目前最广泛使用的授权框架,用于让第三方应用安全地访问用户在某个服务上的资源,而不需要暴露用户的密码
理解 OAuth2 的核心:它解决的问题是”我信任 Google,但我不想把 Google 密码告诉某个第三方 App”
3.网关实现鉴权
二、用户认证
1.连接数据库实现认证
屏蔽原来定义的UserDetailsService
自定义UserDetailsService
2.修改密码为硬编码
1 2 3 4 5 6
| @Bean public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(); }
|
扩展用户身份信息
1 2 3 4 5 6 7 8 9 10
| String password =user.getPassword(); String[] authorities = {"p1"};
user.setPassword(null); String userString = JSON.toJSONString(user); UserDetails userDetails = User.withUsername(userString).password(password).authorities(authorities).build();
|
将用户信息转成json字串直接存入
3.获取用户身份
1 2 3 4 5 6 7
| Object principalObj = SecurityContextHolder.getContext().getAuthentication().getPrincipal(); if (principalObj instanceof String) { String principal = principalObj.toString(); XcUser user = JSON.parseObject(principal, XcUser.class); return user;
|
本质上就是基于上下文取出的
4.统一认证入口
默认的DaoAuthenticationProvider 会进行密码校验,现在重新定义DaoAuthenticationProviderCustom类,重写类的additionalAuthenticationChecks方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| @Slf4j @Component public class DaoAuthenticationProviderCustom extends DaoAuthenticationProvider {
@Autowired public void setUserDetailsService(UserDetailsService userDetailsService) { super.setUserDetailsService(userDetailsService); }
protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
} }
|
接着注入自定义DaoAuthenticationProvider
1 2 3 4 5 6 7 8
| @Autowired DaoAuthenticationProviderCustom daoAuthenticationProviderCustom;
@Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.authenticationProvider(daoAuthenticationProviderCustom); }
|
根据不同类型,取出不同的 bean 执行
1 2 3 4
| String authType = authParamsDto.getAuthType(); AuthService authService = applicationContext.getBean(authType + "_authservice", AuthService.class); XcUserExt user = authService.execute(authParamsDto);
|
5.验证码功能

核心思路:验证码的本质是”临时共享秘密“,前端拿着 key,后端用 key 去 Redis 找答案,比对一致则通过,用完即删6
6.微信验证码
所以我们这里不用所谓的内网穿透也可以成功实现
302重定向 就是微信服务器告诉浏览器:
“你自己去这个地址,code我已经拼在URL里了”
code 从来没有经过微信服务器→你内网这条路,而是:
- 微信服务器 → 浏览器(302响应,code在Location header里)
- 浏览器 → localhost(浏览器自己发的请求,code在URL参数里)