Spring Boot + Vue 前后端分离项目 -- 后端登录接口实现

vue

前言

Spring Boot" title="Spring Boot">Spring Boot + Vue 前后端分离项目中,后端只提供接口,页面处理和跳转都由前端实现,前后端通过 json 传输数据。

后端项目,搭建骨架,可以参考文章:使用 MybatisGenerator 根据数据库自动生成 model、mapper 接口和 mapper.xml

接下来开始后端登录接口的实现。

处理 User 用户类

让 User 类实现接口 UserDetails,并重写其中的方法:

public class User implements Serializable, UserDetails {

......

@Override

public boolean isAccountNonExpired() {

return true; // 账户没有过期

}

@Override

public boolean isAccountNonLocked() {

return true; // 账户没有锁定

}

@Override

public boolean isCredentialsNonExpired() {

return true; // 密码没有过期

}

@Override

public boolean isEnabled() {

return enabled; // 账户可以使用(需要删除 User 类自带的 getEnabled 方法,如果有的话)

}

@Override

public Collection<? extends GrantedAuthority> getAuthorities() {

return null; // 暂不设置

}

......

}

这里的 User 类,就是整个项目存放用户信息的类,有用户名、密码之类的。

UserService 配置

在包 service 下,新建一个 UserService 类,实现接口 UserDetailsService:

@Service

public class UserService implements UserDetailsService {

@Autowired

UserMapper userMapper;

@Override

public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

User user = userMapper.loadUserByUsername(username);

if (user == null){

throw new UsernameNotFoundException("用户名不存在!");

}

return user;

}

}

关键代码:

User user = userMapper.loadUserByUsername(username);

这里重写了 loadUserByUsername 方法,通过用户名加载用户信息。

UserMapper 配置

在接口 UserMapper 中,定义 loadUserByUsername 方法:

public interface UserMapper {

......

User loadUserByUsername(String username);

}

UserMapper.xml 配置

在 UserMapper.xml 实现方法 loadUserByUsername ,只需要添加一个查询语句:

  ......

<select id="loadUserByUsername" resultMap="BaseResultMap">

select * from user where username=#{username};

</select>

......

编写 RespBean 类

RespBean 类主要用于向前端返回数据,会在后端很多地方用到,比如 SpringSecurity 配置中就会用到:

public class RespBean {

private Integer status;

private String msg;

private Object obj;

public static RespBean ok(String msg){

return new RespBean(200, msg, null);

}

public static RespBean ok(String msg, Object obj){

return new RespBean(200, msg, obj);

}

public static RespBean error(String msg){

return new RespBean(500, msg, null);

}

public static RespBean error(String msg, Object obj){

return new RespBean(500, msg, obj);

}

private RespBean() {

}

private RespBean(Integer status, String msg, Object obj) {

this.status = status;

this.msg = msg;

this.obj = obj;

}

public Integer getStatus() {

return status;

}

public void setStatus(Integer status) {

this.status = status;

}

public String getMsg() {

return msg;

}

public void setMsg(String msg) {

this.msg = msg;

}

public Object getObj() {

return obj;

}

public void setObj(Object obj) {

this.obj = obj;

}

}

注意一下,RespBean 类有两个构造函数,都是 private 修饰,表明其他的类就不能直接调用 RespBean 生成新的对象,这样,RespBean 类只有一个对象实例。

这里定义了两个静态方法 ok 和 error ,返回值都是 RespBean 对象。

Spring Security 配置

一般在 Spring Boot + Vue 前后端分离项目中,都是采用 Spring Security 作访问和权限控制。

关于 Spring Security 的权限控制暂且不谈,这里主要是登录接口的配置。

新建一个包 config,再建一个配置类 SecurityConfig ,继承 WebSecurityConfigurerAdapter :

@Configuration

public class SecurityConfig extends WebSecurityConfigurerAdapter {

@Autowired

UserService userService;

// 如果密码采用 BCryptPasswordEncoder 加密,则取消注释

// @Bean

// PasswordEncoder passwordEncoder(){

// return new BCryptPasswordEncoder();

// }

@Override

protected void configure(AuthenticationManagerBuilder auth) throws Exception {

auth.userDetailsService(userService);

}

@Override

protected void configure(HttpSecurity http) throws Exception {

http.authorizeRequests().anyRequest().authenticated()

.and()

.formLogin()

.usernameParameter("username")

.passwordParameter("password")

.loginProcessingUrl("/doLogin")

.loginPage("/login")

.successHandler(new AuthenticationSuccessHandler() {

@Override

public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {

httpServletResponse.setContentType("application/json;charset=utf-8");

PrintWriter writer = httpServletResponse.getWriter();

User user = (User) authentication.getPrincipal();

user.setPassword(null);

RespBean ok = RespBean.ok("登录成功!", user);

String string = new ObjectMapper().writeValueAsString(ok);

writer.write(string);

writer.flush();

writer.close();

}

})

.failureHandler(new AuthenticationFailureHandler() {

@Override

public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {

httpServletResponse.setContentType("application/json;charset=utf-8");

PrintWriter writer = httpServletResponse.getWriter();

RespBean respBean = RespBean.error("登录失败!");

if (httpServletResponse instanceof LockedException){

respBean.setMsg("账户被锁定,请联系管理员!");

}else if (httpServletResponse instanceof BadCredentialsException){

respBean.setMsg("用户名或密码输入错误!");

}else if (httpServletResponse instanceof DisabledException){

respBean.setMsg("账户被禁用,请联系管理员!");

}else if (httpServletResponse instanceof AccountExpiredException){

respBean.setMsg("账户过期,请联系管理员!");

}else if (httpServletResponse instanceof CredentialsExpiredException){

respBean.setMsg("密码过期,请联系管理员!");

}else {

respBean.setMsg("登录失败!");

}

String string = new ObjectMapper().writeValueAsString(respBean);

writer.write(string);

writer.flush();

writer.close();

}

})

.permitAll()

.and()

.logout()

.logoutSuccessHandler(new LogoutSuccessHandler() {

@Override

public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {

httpServletResponse.setContentType("application/json;charset=utf-8");

PrintWriter writer = httpServletResponse.getWriter();

writer.write(new ObjectMapper().writeValueAsString(RespBean.ok("注销登录")));

writer.flush();

writer.close();

}

})

.permitAll()

.and()

.csrf().disable();

}

}

这个配置类比较长,我们挨个分析。

由于我这个数据库中 User 类的密码采用的是 md5DigestAsHex 加密,所以没有用 SpringSecurity 自带的加密方式 BCryptPasswordEncoder。

1、方法 configure(AuthenticationManagerBuilder auth) 解释

代码如下:

    @Override

protected void configure(AuthenticationManagerBuilder auth) throws Exception {

auth.userDetailsService(userService);

}

这个方法身份验证,将登录的用户信息放在 auth 中,通过 userDetailsService 方法设置,传入的参数就是之前配置好的 userService。

2、方法 configure(HttpSecurity http) 解释

这个方法用于处理登录表单,主要分为三大块:

  • 登录成功的配置

  • 登录失败的配置

  • 注销登录的配置

2.1 登录成功的配置

    protected void configure(HttpSecurity http) throws Exception {

http.authorizeRequests().anyRequest().authenticated()

.and()

.formLogin()

.usernameParameter("username")

.passwordParameter("password")

.loginProcessingUrl("/doLogin")

.loginPage("/login")

.successHandler(new AuthenticationSuccessHandler() {

@Override

public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {

httpServletResponse.setContentType("application/json;charset=utf-8");

PrintWriter writer = httpServletResponse.getWriter();

User user = (User) authentication.getPrincipal();

user.setPassword(null);

RespBean ok = RespBean.ok("登录成功!", user);

String string = new ObjectMapper().writeValueAsString(ok);

writer.write(string);

writer.flush();

writer.close();

}

})

......

}

其中

http.authorizeRequests().anyRequest().authenticated()

表明所有请求都需要登录过后才能访问。

                .formLogin()

.usernameParameter("username")

.passwordParameter("password")

.loginProcessingUrl("/doLogin")

.loginPage("/login")

配置登录表的 用户名、密码、处理登录的页面、登录成功的页面。

登录成功后:

                        httpServletResponse.setContentType("application/json;charset=utf-8");

PrintWriter writer = httpServletResponse.getWriter();

User user = (User) authentication.getPrincipal();

user.setPassword(null);

RespBean ok = RespBean.ok("登录成功!", user);

String string = new ObjectMapper().writeValueAsString(ok);

writer.write(string);

writer.flush();

writer.close();

这些配置几乎都是固定的,直接 copy 即可。

还有登录失败的配置、注销登录的配置,可以参考文章:Spring Security 基础教程 -- HttpSecurity 权限和登录表单配置

配置密码加密的类

由于我的数据库中 User 类的密码采用的是 md5DigestAsHex 加密,没有用 SpringSecurity 自带的加密方式 BCryptPasswordEncoder。

所以需要自定义加密类,照样在 config 包下,新建 MyPasswordEncoder 类:

@Component

public class MyPasswordEncoder implements PasswordEncoder {

@Override

public String encode(CharSequence charSequence) {

return DigestUtils.md5DigestAsHex(charSequence.toString().getBytes());

}

@Override

public boolean matches(CharSequence charSequence, String s) {

return s.equals(DigestUtils.md5DigestAsHex(charSequence.toString().getBytes()));

}

}

配置登录成功后的页面

由于 SpringSecurity 在登录成功后,会自动跳转页面,而在 Spring Boot + Vue 前后端分离项目中,跳转页面是前端的事,后端只返回 json 数据即可。

所以需要额外配置一下。

在 controller 包下新建类 LoginController :

@RestController

public class LoginController {

@GetMapping("/login")

public RespBean login(){

return RespBean.error("尚未登录,请登录!");

}

}

测试登录接口

一切准备就绪,需要测试一下效果。

在 controller 包下新建类 HelloController:

@RestController

public class HelloController {

@GetMapping("/hello")

public String hello(){

return "hello";

}

}

这里定义了一个访问接口 /hello,最后用 postman 测试。

postman 测试

  1. 启动项目,在 postman访问 http://localhost:8081/hello,效果如下:

  1. 登录操作

  1. 再访问接口 /hello

  1. 注销登录

注销登录是 get 请求,默认接口时 logout :

每天学习一点点,每天进步一点点。

以上是 Spring Boot + Vue 前后端分离项目 -- 后端登录接口实现 的全部内容, 来源链接: utcz.com/z/376898.html

回到顶部