1.创建项目
1. 导入相关依赖
除此之外还需要手动引入Druid连接池
的依赖
<!--手动导入druid连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
<build>
<!--Mapper.xml和mapper在同一个目录需要配置resources-->
<resources>
<resource>
<directory>src/main/resources</directory>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
</resources>
</build>
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.8</version>
<scope>provided</scope>
</dependency>
2. 修改配置文件
spring:
#配置数据库相关信息
datasource:
type: com.alibaba.druid.pool.DruidDataSource
data-username: racs
password: asfla123
url: jdbc:mysql://rm-2ze4f9fp157q2msjkasf5o.mysql.rds.aliyuncs.com:3306/数据库名称?useUnicode=true&characterEncoding=utf8
#解决字符编码
http:
encoding:
charset: utf-8
force-request: true
force-response: true
server:
port: 8081
tomcat:
uri-encoding: UTF-8
3. 文件目录结构
4. 编写Member实体类
package org.magic.login.entiy;
import java.util.Collection;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
/**
* Member
*
* @author LQ
* @Date 创建时间:2020-04-21 13:49:38
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Member implements UserDetails {
private static final long serialVersionUID = 1L;
//columns START
/**
* id
*/
private String id;
/**
* 姓名
*/
private String name;
/**
* 手机号码
*/
private String phone;
/**
* 住宅电话
*/
private String telephone;
/**
* 联系地址
*/
private String address;
/**
* enabled
*/
private Boolean enabled;
/**
* 用户名
*/
private String username;
/**
* 密码
*/
private String password;
/**
* userface
*/
private String userface;
/**
* remark
*/
private String remark;
public Collection<? extends GrantedAuthority> getAuthorities() {
return null;
}
public boolean isAccountNonExpired() {
return true;
}
public boolean isAccountNonLocked() {
return true;
}
public boolean isCredentialsNonExpired() {
return true;
}
public boolean isEnabled() {
return true;
}
}
5. 编写响应实体类
package org.magic.login.entiy;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class RespDataRv {
/**
* 状态
*/
private Integer status;
/**
* 信息
*/
private String msg;
/**
* 对象
*/
private Object obj;
public static RespDataRv success(String msg) {
return new RespDataRv(200, msg, null);
}
public static RespDataRv success(String msg, Object obj) {
return new RespDataRv(200, msg, obj);
}
public static RespDataRv error(String msg) {
return new RespDataRv(500, msg, null);
}
public static RespDataRv error(String msg, Object obj) {
return new RespDataRv(500, msg, obj);
}
}
6. 编写SecurityConfig
package org.magic.login.config;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.magic.login.entiy.Member;
import org.magic.login.entiy.RespDataRv;
import org.magic.login.service.MemberService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AccountStatusException;
import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.LockedException;
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.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private MemberService memberService;
//编码密码
@Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(memberService);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.formLogin()
.usernameParameter("username")
.passwordParameter("password")
.loginProcessingUrl("/toLogin")
//如果没有登录,则会跳转到登录页面
.loginPage("/login")
.successHandler(new AuthenticationSuccessHandler() {
/**
* 如果登录成功,则返回此信息
*
* @param req
* @param resp
* @param authentication
* @throws IOException
* @throws ServletException
*/
public void onAuthenticationSuccess(HttpServletRequest req, HttpServletResponse resp, Authentication authentication) throws IOException, ServletException {
resp.setContentType("application/json;charset=utf-8");
PrintWriter writer = resp.getWriter();
Member member = (Member) authentication.getPrincipal();
RespDataRv success = RespDataRv.success("登录成功!", member);
String str = new ObjectMapper().writeValueAsString(success);
writer.write(str);
writer.flush();
writer.close();
}
}).failureHandler(new AuthenticationFailureHandler() {
/**
* 如果登录失败,则返回此信息
*
* @param req
* @param resp
* @param exception
* @throws IOException
* @throws ServletException
*/
public void onAuthenticationFailure(HttpServletRequest req, HttpServletResponse resp, AuthenticationException exception) throws IOException, ServletException {
resp.setContentType("applicaiton/json;charset=utf-8");
PrintWriter writer = resp.getWriter();
RespDataRv respDataRv = RespDataRv.error("登录失败!");
if (exception instanceof LockedException) {
respDataRv.setMsg("账户被锁定,请联系管理人员!");
} else if (exception instanceof BadCredentialsException) {
respDataRv.setMsg("用户名或密码输入有误,请重新输入!");
} else if (exception instanceof DisabledException) {
respDataRv.setMsg("账户已过期,请联系管理员!");
} else if (exception instanceof AuthenticationCredentialsNotFoundException) {
respDataRv.setMsg("账户未进行身份认证,请联系管理员!");
} else if (exception instanceof AccountStatusException) {
respDataRv.setMsg("账户状态异常,请联系管理员!");
} else {
respDataRv.setMsg("登录发生未知错误,请联系管理员!");
}
writer.write(new ObjectMapper().writeValueAsString(respDataRv));
writer.flush();
writer.close();
}
}).permitAll()
.and()
.logout()
.logoutSuccessHandler(new LogoutSuccessHandler() {
/**
* 注销登录,返回此信息
*
* @param req
* @param resp
* @param authentication
* @throws IOException
* @throws ServletException
*/
public void onLogoutSuccess(HttpServletRequest req, HttpServletResponse resp, Authentication authentication) throws IOException, ServletException {
resp.setContentType("application/json;charset=utf-8");
PrintWriter writer = resp.getWriter();
RespDataRv success = RespDataRv.success("注销成功!");
writer.write(new ObjectMapper().writeValueAsString(success));
writer.flush();
writer.close();
}
}).permitAll()
.and()
.csrf()
.disable();
}
}
7. 编写Controller
登录后可以访问的hello接口
package org.magic.login.controller;
import org.magic.login.entiy.RespDataRv;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@GetMapping("/hello")
public Object hello() {
return RespDataRv.success("hello world");
}
}
登录接口:
package org.magic.login.controller;
import org.magic.login.entiy.RespDataRv;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class LoginController {
@GetMapping("/login")
public Object login() {
return RespDataRv.error("兄弟,你还没有登陆!");
}
}
8. 编写Service
package org.magic.login.service;
import org.magic.login.entiy.Member;
import org.magic.login.mapper.MemberMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
/**
* @author liqiang
*/
@Service
public class MemberService implements UserDetailsService {
@Autowired
private MemberMapper memberMapper;
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Member member = memberMapper.loadUserByUsername(username);
if (member == null) {
throw new UsernameNotFoundException("用户名不存在!");
}
return member;
}
}
9. 编写Mapper
package org.magic.login.mapper;
import org.apache.ibatis.annotations.Mapper;
import org.magic.login.entiy.Member;
import org.springframework.stereotype.Component;
@Mapper
@Component
public interface MemberMapper {
Member loadUserByUsername(String username);
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="org.magic.login.mapper.MemberMapper">
<resultMap id="Member" type="org.magic.login.entiy.Member">
<result property="id" column="id" jdbcType="INTEGER"/>
<result property="name" column="name" jdbcType="VARCHAR"/>
<result property="phone" column="phone" jdbcType="CHAR"/>
<result property="telephone" column="telephone" jdbcType="VARCHAR"/>
<result property="address" column="address" jdbcType="VARCHAR"/>
<result property="enabled" column="enabled" jdbcType="BIT"/>
<result property="username" column="username" jdbcType="VARCHAR"/>
<result property="password" column="password" jdbcType="VARCHAR"/>
<result property="userface" column="userface" jdbcType="VARCHAR"/>
<result property="remark" column="remark" jdbcType="VARCHAR"/>
</resultMap>
<!--自己的sql-->
<select id="loadUserByUsername" resultMap="Member">
select * from member o where userName=#{userName}
</select>
</mapper>
2.使用Postman测试
1. 访问一个需要处于登录状态的接口
2. 输入账号密码登录
3. 再次访问hello接口
4. 注销登录
5. 再次访问hello接口
3. 可能遇到的问题
- 在登录测试的时候,老是显示账号被锁定
这是我们自己处理的异常,将异常打印出来显示如下:
原因是Member实体类在实现UserDetails的时候,重写的4个方法最终return的是false
,
将false
都改为true
就可以正常登录了。