自定义登录
我们之前一直使用的是Spring Security给我们默认的登录页面,不好看不说,还不能扩展。它还在每次测试功能时在那转很久,所以我们需要一个自定义的登录页面。
1.首先我们的登录页面,form有满足四个条件
- 它是一个post请求
- 请求路径是/login
- 姓名的name属性叫username
- 密码的name属性叫password
在resources下的static创建login.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Login Page</title>
<style>
body {
background-color: #F5F5F5;
font-family: Arial, Helvetica, sans-serif;
}
#login-form {
background-color: #FFFFFF;
max-width: 480px;
margin: 0 auto;
padding: 30px;
border-radius: 5px;
box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.1);
}
h1 {
text-align: center;
color: #333333;
}
input[type="text"], input[type="password"] {
display: block;
width: 100%;
padding: 10px;
margin-bottom: 20px;
border-radius: 3px;
border: none;
box-shadow: 0px 0px 5px rgba(0, 0, 0, 0.1);
}
input[type="submit"] {
display: block;
width: 100%;
background-color: #4CAF50;
color: #FFFFFF;
padding: 10px;
font-size: 16px;
border: none;
border-radius: 3px;
cursor: pointer;
}
input[type="submit"]:hover {
background-color: #3E8E41;
}
</style>
<script src="./js/index.js"></script>
</head>
<body>
<form id="login-form" action="/toLogin" method="post">
<h1>登录页面</h1>
<label>用户:</label>
<input type="text" placeholder="请输入账户" name="username" required>
<label>密码:</label>
<input type="password" placeholder="请输入密码" name="password" required>
<input type="checkbox" name="remember-me" id="remember-me">记住密码
<input type="submit" value="登录">
</form>
</body>
</html>
2.在配置类中做配置
@Autowired
private MySuccessHandler mySuccessHandler;
@Autowired
private MyFailureHandler myFailureHandler;
@Bean
public SecurityFilterChain chain(HttpSecurity http) throws Exception {
return http
.csrf().disable()
.authorizeRequests()
.anyRequest().authenticated()
.and()
//*
.formLogin()//首先,我们要将登录格式改为表单
.loginPage("/login.html")//用来指定默认登录界面,一旦自定义登录页面以后必须只能登录url
.loginProcessingUrl("/toLogin")//指定处理登录请求 url。前面说请求路径是/login,在这里可以修改。
.successHandler(mySuccessHandler)//方法是定义一个认证成功后的处理逻辑。在认证成功后,我们可以重定向到其他页面或设置一些用户信息等
.failureHandler(myFailureHandler)//自定义认证失败后的处理
.permitAll()//公告资源(所用用户都可以使用)
//*
.and()
.build();
}
/*
还有很多可以配置,都可以试一试
.usernameParameter() 自定义姓名name属性名字
.passwordParameter() 自定义密码name属性名字
.successForwardUrl() 认证成功后跳转页面到指定页面
.defaultSuccessUrl() 认证成功后跳转页面到指定页面,与上面的区别是:重定向,会记录被拦截前的页面认证成功后跳转到之前的页面,如果之前没有页面就跳转到指定页面
*/
3.认证成功处理和失败处理
在前面的学习中,我们知道UsernamePasswordAuthenticationFilter成功执行AuthenticationSuccessHandler,失败执行AuthenticationFailureHandler,所以我们分别实现他们俩个好了。
@Configuration
public class MySuccessHandler implements AuthenticationSuccessHandler {
@Autowired
private ObjectMapper objectMapper;
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
response.getWriter().println(objectMapper.writeValueAsString(HttpResult.ok("登录成功")));
}
}
@Configuration
public class MyFailureHandler implements AuthenticationFailureHandler {
@Autowired
private ObjectMapper objectMapper;
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
PrintWriter writer = response.getWriter();
String JsonResult = null;
if(exception instanceof LockedException){
JsonResult = objectMapper.writeValueAsString(HttpResult.fail(999,"账户被锁定!"));
}
else if(exception instanceof UsernameNotFoundException){
JsonResult = objectMapper.writeValueAsString(HttpResult.fail(998,"用户不存在"));
}
else if(exception instanceof BadCredentialsException){
JsonResult = objectMapper.writeValueAsString(HttpResult.fail(997,"密码错误"));
}
writer.write(JsonResult);
}
}
自定义注销功能
1.在配置类中做配置
@Autowired
private MyLogoutSuccessHandler myLogoutSuccessHandler;
@Bean
public SecurityFilterChain chain(HttpSecurity http) throws Exception {
return http
.csrf().disable()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.logout()
.logoutRequestMatcher(new OrRequestMatcher(
new AntPathRequestMatcher("/toLogout","GET"),
new AntPathRequestMatcher("/exit","POST")
))//自定义注销登录,可以写多个请求
.invalidateHttpSession(true) //默认true 会话失效
.clearAuthentication(true) //默认true 清除认证标记
.logoutSuccessHandler(myLogoutSuccessHandler) //自定义注销处理
.permitAll()
.and()
.build();
}
/*
logoutSuccessUrl("/test") 注销登录 成功之后跳转页面
logoutUrl("/logout") 指定注销登录url默认请求方式必须GET
*/
2.自定义注销处理
@Configuration
public class MyLogoutSuccessHandler implements LogoutSuccessHandler {
@Autowired
private ObjectMapper objectMapper;
@Override
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
response.getWriter().println(objectMapper.writeValueAsString(HttpResult.ok("注销成功")));
}
}
配置文件
@Configuration
@EnableWebSecurity
public class SecurityConfig{
@Bean
public SecurityFilterChain chain(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/login").anonymous()
.anyRequest().authenticated()
return http.build();
}
}
/*
anonymous() 匿名放行资源(匿名用户所使用的)
permitAll() 公告资源(所用用户都可以使用)
anyRequest().authenticated() 代表所用请求,必须认证之后才能访问
formLogin() 代表开启表单验证
注意:放行资源必须放在所在请求之前
*/
/*提供了一种抽象的方式来自定义 Spring Security 的配置,可以用于修改 WebSecurityConfigurerAdapter 中定义的默认安全策略以及添加自定义的安全策略。
放行/js和/css中的文件
*/
@Bean
public WebSecurityCustomizer customizer(){
return web -> web.ignoring().antMatchers("/js/**","css/**");
}
权限功能
配置类
@EnableGlobalMethodSecurity(prePostEnabled = true)//在配置类加上这个注解
让后在需要权限的接口上添加权限即可
@GetMapping("/")
@PreAuthorize("hasAuthority('read')")
public HttpResult user(){
return HttpResult.ok("hello user","cgl");
}
//这个接口就需要携带‘read’权限的用户才可以访问。
自定义hasAuthority
@Component("root")
public class MyExpressionRoot {
public boolean hasAuthority(String authority){
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
boolean contains = authorities.contains(new CustomAuthority(authority));
return contains;
}
}
//自定义的好处在与更加灵活,可以实现更多功能