1.初次使用
模块名称 | default-demo |
---|---|
功能描述 | 完成登录后,才能对接口进行访问 |
1.1依赖导入
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--security-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
1.2配置类
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
}
1.3Controller接口
@RestController
public class DefaultDemoController {
@GetMapping("query")
public String query(){
return "stu list";
}
}
1.4启动后控制台
1.5通过浏览器访问 query 接口
①浏览器输入:localhost:8080/query
②页面会自动跳转到:一个登录页(localhost:8080/login)
③输入用户名:user,用户密码:f30bcf90-bd54-4ae0-a934-5dc66ba8dd34(控制台打印出来的)进行登录,即可完成登录,并自动跳转到 query接口
1.6框架做了什么?
①用户输入localhost:8080/query
②此接口请求被springSecurity框架中的过滤器拦截,并重定向页面到一个(localhost:8080/login)上。
③用户输入了用户名和密码
④SpringSecurity框架中的代码进行用户名密码的校验。
⑤校验失败,返回登录失败
⑥校验成功,跳转到之前访问的 query接口上
1.7以上流程中需要自定义东西
①当用户未登录时,我希望跳转到我指定的登录页面。
②用户的校验逻辑需要我自己去定义(查询数据库校验)
2.自定义登录页面
2.1定义一个登录页面
2.2修改配置类
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private MyLoginFailureHandler myLoginFailureHandler;
@Override
protected void configure(HttpSecurity http) throws Exception {
//页面名称必须加 /
http.formLogin().loginPage("/mylogin.html")
.and()
.authorizeRequests().antMatchers("/mylogin.html").permitAll()
.and()
.authorizeRequests().anyRequest().authenticated(); //所有的请求都需要认证后访问
}
}
2.3访问query接口
2.4进行登录
没有效果,仍然停留在 mylogin.html这个登录页。
问题原因:指定了登录页面,但是没有指定 处理登录逻辑的 url地址
解决方式修改配置文件
2.5表单字段不一致问题
①解决方式1:将字段名成换成username、password
②解决方式2:修改SpringSecurity在登录时获取的参数名称
2.6还是登录 不了
2.7登录失败后的逻辑
默认逻辑是跳转到 mylogin?error页面
设置一个登录失败处理器
@Component
public class MyLoginFailureHandler implements AuthenticationFailureHandler {
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
response.getWriter().println("登录失败");
}
}
2.8登录成功后的逻辑
默认是跳转回原页面、或者回到 主页面(index.html)
配置
@Component
public class MyLoginSuccessHandler implements AuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
response.setCharacterEncoding("UTF-8");
response.getWriter().println("登录成功");
}
}
3.自定义登录逻辑
在真实的场景中,项目会有一个用户信息,里面有用户的密码,用户名登录信息。这些信息一定是从数据库中取得的。
3.1原始SpringSecurity框架在我们点击登录后为我们做了什么
1.首先进入 UsernamePasswordAuthenticationFilter
的 public ``Authentication ``attemptAuthentication``_(_``HttpServletRequest request``,`` ``HttpServletResponse response``_) _``throws ``AuthenticationException
的方法中。方法做了以下事情
①获取用户名、密码
②生成UsernamePasswordAuthenticationToken
③调用 AuthenticationManager去认证这个 UnamepwdAuthenToken
2.Authentication的实现类(ProviderManager
),执行自己的 public Authentication authenticate(Authentication authentication)
,这个方法中做了以下事务
①获取这个 unamepwdToken的字节码对象
②寻找一个可以认证这个 unamepwdToken的 Provider
③调用provider的认证方法,返回Authenticaiton
对象
3.Provider是进行unamepwdToken的验证,其中一个实现类(RemoteAuthenticationProvider
)provider的support方法用来判断unamepwdtokne是否是我所能够处理的类型。接下来看下Provider的处理方法:
①从unametoken中去得用户名
②从Credentials中去得用户的密码
③通过认证 Manager中尝试授权,获取 角色集合(这里用的就是 UserDetailService,并返回一个UserDetail对象)
④将用户名密码、和角色重新封装成 unamepwdToken
4.UserDetailService提供的就是根据用户名查询 用户角色的功能,并返回一个角色的集合的方法
3.2需求描述
1.query方法需要有Admin角色才能够访问,
2.数据库中有 sysUser表和role表
3.重写UserDetailService和UserDetail
3.3角色设置了,不起作用的原因
3.4三种权限注解
①JSR-250注解:@DenyAll @RoleAllows()@PermitAll
②。。。
4.图片验证码
1.设置前置过滤器进行校验
2.实现认证失败处理类,完成认证失败时的返回
5.自定义登录逻辑
模仿智联招聘登录,用户仅凭手机号和验证码就可以实现登录
①短信验证码,使用图形验证码模拟
②框架登录逻辑需要密码比对
5.1编写处理短信验证码的过滤器
5.2编写SmsToken
5.3编写Provider
5.4编写在Provider中使用的UserDetailService以及UserDetail
在provider中的authenticate方法中会调用 userDetailService的loginByusername,返回userDetail
6.用户名密码登录的逻辑流程(源码级别)
1.登录请求进入UsernamePasswordAuthenticationFilter中。
2.获取用户名、密码封装成一个未认证的 token
3.将token放入 认证管理中 进行认证
4.认证管理器的一个实现类(providerManager)的authenticate方法
5.providermanager选择一个支持这种token的Provider进行校验
6.用户密码选择 了一个 DaoAuthenticationProvider(实现了AbstractUserDetailsAuthenticationProvider)的一个实体类
7.DaoAuthenticationProvider中的 authenticate方法做了:
①从token的principles字段获取用户名
String username = (authentication.getPrincipal() == null) ? "NONE_PROVIDED"
: authentication.getName();
②先尝试从缓存中获取
③调用retriveuser方法
protected final UserDetails retrieveUser(String username,
UsernamePasswordAuthenticationToken authentication)
throws AuthenticationException {
prepareTimingAttackProtection();
try {
//通过username查询用户的 信息 (UserDetail)
UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username);
if (loadedUser == null) {
throw new InternalAuthenticationServiceException(
"UserDetailsService returned null, which is an interface contract violation");
}
//返回UserDetail信息
return loadedUser;
}
catch (UsernameNotFoundException ex) {
mitigateAgainstTimingAttack(authentication);
throw ex;
}
catch (InternalAuthenticationServiceException ex) {
throw ex;
}
catch (Exception ex) {
throw new InternalAuthenticationServiceException(ex.getMessage(), ex);
}
}
④通过preAuthenticationChecks检查用户是否过期,禁用之类
preAuthenticationChecks.check(user);
⑤additionalAuthenticationChecks 密码校验
// 从数据库中查询的信息,token信息
additionalAuthenticationChecks(user,(UsernamePasswordAuthenticationToken) authentication);
==============================================================================
protected void additionalAuthenticationChecks(UserDetails userDetails,
UsernamePasswordAuthenticationToken authentication)
throws AuthenticationException {
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"));
}
}
⑥返回一个Tokne,pricipal就是 从数据库中查询的userDetail,authentication是原始token,从中去得密码,user也是userDetail查询的。
7.Oauth2.0授权码模式
这里插入一条关于权限注解的相关内容,使用注解时,不要再用http.对请求进行指定了。
7.1项目搭建
1.pom文件导入
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
2.Security配置文件
@EnableGlobalMethodSecurity(prePostEnabled = true,securedEnabled = true,jsr250Enabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
}
3.认证服务器配置文件
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig {
}
4.controller的几个方法
@RestController
public class UserController {
/**
* 回调方法
* @param code
* @return
*/
@RequestMapping("getCode")
public String getCode(String code){
return code;
}
}
5.配置文件
security:
oauth2:
client:
client-id: yurun
client-secret: yurun
registered-redirect-uri: http://localhost:8080/getCode
scope: all
7.2运行步骤
1.通过浏览器访问(http://localhost:8080/oauth/authorize?response_type=code&client_id=yurun&redirct_uri=http://localhost:8080/getCode&scope=all)
2.跳转到登录页面,并完成登录
3.输入用户名和密码,会跳转到 getcode接口,并携带一个code
4.请求token
5.返回的结果
{
"access_token": "abe04eb7-5dbe-4bb5-8606-68049436cdb4",
"token_type": "bearer",
"refresh_token": "03e96129-f94f-4537-bb00-d4eeb56c1022",
"expires_in": 43199,
"scope": "all"
}
8.密码模式
8.1请求token
8.2报错
8.3对SecurityConfig配置类修改
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
9.资源服务器的配置
9.1在认证服务器上同时设有资源服务器
@Configuration
@EnableResourceServer
public class ResourceServerConfig {
}
9.2产生问题
资源服务器的存在会让其服务器上的所有接口需要令牌反问,当然包括 login接口。所以我们需要设置login接口。被允许
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http.formLogin().and().authorizeRequests().antMatchers("/login").permitAll();
}
}