对于登录接口,登录成功后的响应,登录失败后的响应,我们都可以在 WebSecurityConfigurerAdapter 的实现类中进行配置。例如下面这样
(以下内容是springboot项目配置好基础spring security以后的内容,spring security的基本配置这里就不重复了。)
下面来详细解释配置的代码:
以下定义了两个访问路径分别所需的权限,除了以admin和user开头的路径其他路径的当问不需要权限:
.antMatchers("/admin/**").hasRole("admin")
.antMatchers("user/**").hasAnyRole("admin","user")
.anyRequest().authenticated()
设置登陆接口,如果用postman测试需要这个接口:
.and()
.formLogin()
.loginProcessingUrl("/doLogin") //登陆接口
当我们访问需要登陆的路径时会自动跳转到跳转到/login路径
.loginPage("/login") //登陆页面
定义登陆时,用户名的key默认是username,这里改为uname
密码的key默认是password,这里改为passwd。
.usernameParameter("uname")
.passwordParameter("passwd")
我们可以在 successHandler 方法中,配置登录成功的回调,如果是前后端分离开发的话,登录成功后返回 JSON 即可
.successHandler(new AuthenticationSuccessHandler() {
@Override
public void onAuthenticationSuccess(HttpServletRequest req, HttpServletResponse resp,
Authentication authentication) throws IOException, ServletException {
设置数据格式为json类型
resp.setContentType("application/json;charset=utf-8");
这里配置登陆成功之后的状态码以及信息
PrintWriter out = resp.getWriter();
Map map = new HashMap<>();
map.put("status",200);
map.put("msg",authentication.getPrincipal());
out.write(new ObjectMapper().writeValueAsString(map));
登陆失败和成功的配置道理是一样的,只是多了登陆失败具体原因的判断:
如下针对登陆失败的不同原因进行具体解释(以便于我们及时查错)
if (e instanceof LockedException) {
map.put("msg","账户被锁定,登陆失败!");
} else if(e instanceof BadCredentialsException) {
map.put("msg","用户名或密码输入错误,登陆失败!");
} else if (e instanceof DisabledException) {
map.put("msg","账户被禁用,登陆失败!");
} else if (e instanceof AccountExpiredException) {
map.put("msg","账户过期,登陆失败!");
} else if (e instanceof CredentialsExpiredException) {
map.put("msg","密码过期,登陆失败!");
} else {
map.put("msg","登陆失败");
}
完整代码:
package org.javaboy.security.config;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.*;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
//告诉系统不加密
@Bean
PasswordEncoder passwordEncoder() {
return NoOpPasswordEncoder.getInstance();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("javaboy").password("123").roles("admin")
.and()
.withUser("江南一点雨").password("456").roles("user");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/admin/**").hasRole("admin")
.antMatchers("user/**").hasAnyRole("admin","user")
.anyRequest().authenticated()
.and()
.formLogin()
.loginProcessingUrl("/doLogin") //登陆接口
.loginPage("/login") //登陆页面
.usernameParameter("uname")
.passwordParameter("passwd")
.successHandler(new AuthenticationSuccessHandler() {
@Override
public void onAuthenticationSuccess(HttpServletRequest req, HttpServletResponse resp,
Authentication authentication) throws IOException, ServletException {
resp.setContentType("application/json;charset=utf-8");
PrintWriter out = resp.getWriter();
Map map = new HashMap<>();
map.put("status",200);
map.put("msg",authentication.getPrincipal());
out.write(new ObjectMapper().writeValueAsString(map));
out.flush();
out.close();
}
})
.failureHandler(new AuthenticationFailureHandler() {
@Override
public void onAuthenticationFailure(HttpServletRequest req, HttpServletResponse resp,
AuthenticationException e) throws IOException, ServletException {
resp.setContentType("application/json;charset=utf-8");
PrintWriter out = resp.getWriter();
Map map = new HashMap<>();
map.put("status",401);
if (e instanceof LockedException) {
map.put("msg","账户被锁定,登陆失败!");
} else if(e instanceof BadCredentialsException) {
map.put("msg","用户名或密码输入错误,登陆失败!");
} else if (e instanceof DisabledException) {
map.put("msg","账户被禁用,登陆失败!");
} else if (e instanceof AccountExpiredException) {
map.put("msg","账户过期,登陆失败!");
} else if (e instanceof CredentialsExpiredException) {
map.put("msg","密码过期,登陆失败!");
} else {
map.put("msg","登陆失败");
}
out.write(new ObjectMapper().writeValueAsString(map));
out.flush();
out.close();
}
})
.permitAll()
.and()
.csrf().disable();
}
}
接口编写相应的跳转页面:
package org.javaboy.security.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello() {
return "hello security";
}
@GetMapping("/admin/hello")
public String admin() {
return "hello admin!";
}
@GetMapping("/user/hello")
public String user() {
return "hello user!";
}
@GetMapping("/login")
public String login() {
return "please login!";
}
}
启动项目,下面用postman进行测试:
先不登陆,随便访问一个需要登陆之后才能跳转的页面,看看spring security会不会自动帮我们跳转到登陆提示界面:
可以看到系统跳转到了/login接口,该接口返回的语句与postman中返回的语句相同,证明配置成功。
接下来,我们访问登陆接口进行登陆,注意这里登陆用户名和密码的key默认值都已经更改了:
登陆成功页面显示如下:
登陆好之后我们再来访问之前路径:
可以看到访问的路径和上面的是一样的,但是结果不再是please login!了,访问成功。