前端的登录页面写完了之后呢,开始写后台的登录接口了,后台的登录接口是用Spring Security写的
1、创建一个vhr的spring boot项目,并添加spring web、mysql、mybatis和spring srcurity的依赖
2、在pom文件里面添加mysql的版本号和阿里巴巴的druid连接池依赖
com.alibaba
druid-spring-boot-starter
1.1.10
mysql
mysql-connector-java
runtime
5.1.27
3、要用mybatis逆向工具把实体类和mapper类生成出来,生成的效果如下图所示
4、要在启动类上面加上@MapperScan=@MapperScan(basePackages = "com.lqg.vhr.mapper")
@SpringBootApplication
@MapperScan(basePackages = "com.lqg.vhr.mapper")
public class VhrApplication {
public static void main(String[] args) {
SpringApplication.run(VhrApplication.class, args);
}
}
5、再在pom文件的build标签里面配一个resource,因为我的xml文件是写在mapper包下面,打包的时候有可能会过滤掉,所以要配resource,配置代码如下
src/main/resources
src/main/java
**/*.xml
org.springframework.boot
spring-boot-maven-plugin
6、在application.properties的配置文件里面配置数据库连接属性
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.username=root
spring.datasource.password=123456
#如果需要把数据库部署到linux服务器上,需要加上?useUnicode=true&characterEncoding=UTF-8,确保连接传输的时候的编码也是utf-8
spring.datasource.url=jdbc:mysql://localhost:3306/vur?useUnicode=true&characterEncoding=UTF-8
1、在Hr实体类里面实现UserDetails接口并重写它的方法,用Spring Security自定义登录接口的话就需要让hr类实现UserDetails,如下代码
/**
* hr
* @author
*如果我们需要从数据库里面加载用户,那么我们在定义用户的时候就要实现一个接口UserDetails
* UserDetails想当于是一个规范,所有人都得实现它,这样的话以后我要获取用户名我就知道就是username,我要获取密码就是password
*/
public class Hr implements UserDetails {
/**
* hrID
*/
private Integer id;
/**
* 姓名
*/
private String name;
/**
* 手机号码
*/
private String phone;
/**
* 住宅电话
*/
private String telephone;
/**
* 联系地址
*/
private String address;
/**
* 是否可用
*/
private Boolean enabled;
/**
* 用户名
*/
private String username;
/**
* 密码
*/
private String password;
private String userface;
private String remark;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public String getTelephone() {
return telephone;
}
public void setTelephone(String telephone) {
this.telephone = telephone;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public void setEnabled(Boolean enabled) {
this.enabled = enabled;
}
@Override
public String getUsername() {
return username;
}
@Override
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getUserface() {
return userface;
}
public void setUserface(String userface) {
this.userface = userface;
}
public String getRemark() {
return remark;
}
public void setRemark(String remark) {
this.remark = remark;
}
@Override
public String toString() {
return "Hr{" +
"id=" + id +
", name='" + name + '\'' +
", phone='" + phone + '\'' +
", telephone='" + telephone + '\'' +
", address='" + address + '\'' +
", enabled=" + enabled +
", username='" + username + '\'' +
", password='" + password + '\'' +
", userface='" + userface + '\'' +
", remark='" + remark + '\'' +
'}';
}
/**
* 账户是否没有过期
* @return
*/
@Override
public boolean isAccountNonExpired() {
return true;
}
/**
* 账户是否被锁定
* @return
*/
@Override
public boolean isAccountNonLocked() {
return true;
}
/**
* 密码是否没有过期
* @return
*/
@Override
public boolean isCredentialsNonExpired() {
return true;
}
/**
* 是否可用
* @return
*/
@Override
public boolean isEnabled() {
return enabled;
}
public void setUsername(String username) {
this.username = username;
}
/**
*返回用户的所有角色
* @return
*/
@Override
public Collection extends GrantedAuthority> getAuthorities() {
return null;
}
}
2、创建一个service包,并创建一个HrService类,该类实现了UserDetailsService类,并重写了该类的loadUserByUsername方法,把HrMapper用
@Autowired
注解注入进来,详细代码如下:
@Service
public class HrService implements UserDetailsService {
@Autowired
HrMapper hrMapper;
/**
* 根据用户名加载user对象
* @param username
* @return
* @throws UsernameNotFoundException
*/
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Hr hr = hrMapper.loadUserByUsername(username);
// 判断用户名是否为空,为空则抛出异常
if (username==null){
throw new UsernameNotFoundException("用户名不存在!");
}
return hr;
}
}
3、在HrMapper接口中添加loadUserByUsername方法,代码如下
@Repository
public interface HrMapper {
int deleteByPrimaryKey(Integer id);
int insert(Hr record);
int insertSelective(Hr record);
Hr selectByPrimaryKey(Integer id);
int updateByPrimaryKeySelective(Hr record);
int updateByPrimaryKey(Hr record);
/**
* 通过用户名查找用户
* @param username
* @return
*/
Hr loadUserByUsername(String username);
}
4、在HrMapper.xml文件中加上如下代码
5、重新创建一个config包,并创建一个SecurityConfig类,该类继承了WebSecurityConfigurerAdapter
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
HrService hrService;
@Bean
PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
//要有一个configure方法吧hrService整进来
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(hrService);
}
}
6、创建一个controller包,并创建一个HelloController类,代码如下:
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello(){
return "欢迎进入登录页面!";
}
}
7、登录成功的话,前端是json,前后端分离之后,前端的页面跳转由前端自己去控制,后端只需提供接口,后端只需要返回json数据就行
8、在SecurityConfig里面继续进行配置登录成功或者登录失败向前端传送json数据
①、重写configure方法
@Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
}
②、通过http.authorizeRequests()进行配置其他请求都是登录之后才能访问的、表单登录、修改默认登录的username、password、处理表单登录的url路径、处理表单登录的url路径、
登录成功的处理、登录失败的处理、跟登录相关的接口就能直接访问、注销成功后的回调、
注销成功后的回调
//配置登录成功或者登录失败向前端传送json数据
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
//剩下的其他请求都是登录之后就能访问的
.anyRequest().authenticated()
.and()
//表单登录
.formLogin()
//修改默认登录的username
.usernameParameter("username")
//修改默认登录的password
.passwordParameter("password")
//处理表单登录的url路径
.loginProcessingUrl("/doLogin")
//默认看到的登录页面,如果是前后端分离的话,就不用处理表单登录的url路径
.loginPage("/login")
//登录成功的处理
.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();
//登录成功的hr对象
Hr hr = (Hr)authentication.getPrincipal();
RespBean ok = RespBean.ok("登录成功!", hr);
//把hr写成字符串
String s = new ObjectMapper().writeValueAsString(ok);
//把字符串写出去
out.write(s);
out.flush();
out.close();
}
})
//登录失败的处理
.failureHandler(new AuthenticationFailureHandler() {
@Override
public void onAuthenticationFailure(HttpServletRequest req, HttpServletResponse resp, AuthenticationException exception) throws IOException, ServletException {
//如果登录成功就返回一段json
resp.setContentType("application/json;charset=utf-8");
//这是往出写的
PrintWriter out = resp.getWriter();
RespBean respBean = RespBean.error("登录失败!");
if(exception instanceof LockedException){
respBean.setMsg("账户被锁定,请联系管理员!");
}else if (exception instanceof CredentialsExpiredException){
respBean.setMsg("密码过期,请联系管理员!");
}else if (exception instanceof AccountExpiredException){
respBean.setMsg("账户过期,请联系管理员!");
}else if (exception instanceof DisabledException){
respBean.setMsg("账户被禁用,请联系管理员!");
}else if (exception instanceof BadCredentialsException){
respBean.setMsg("用户名或者密码输入错误,清联系管理员!");
}
out.write(new ObjectMapper().writeValueAsString(respBean));
out.flush();
out.close();
}
})
//跟登录相关的接口就能直接访问
.permitAll()
.and()
.logout()
//注销成功后的回调
.logoutSuccessHandler(new LogoutSuccessHandler() {
@Override
public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
}
})
.permitAll()
.and()
//关闭csrf攻击
.csrf().disable();
}
9、通过postman进行测试
访问hello接口,出现404,本来应该出来401的,但是访问hello接口的时候,它发现你没有登录,所以要你去登录页,SecurityConfig类里面没有配置登录页,所以就报404
我再去访问doLogin,访问结果图片如下:
登录成功,再在postman里面测试hello接口
访问成功
1、因为访问hello接口返回的是404,所以要把页面改成返回成json格式的数据,重新创建一个LoginController,代码如下:
@RestController
public class LoginController {
@GetMapping("/login")
public RespBean login(){
return RespBean.error("尚未登录,请登录!")
}
}
2、再把SecurityConfig配置类的注销成功后的回调完善一下,首先设置返回类型为json类型,然后使用resp.getWrite()方法进行输出,代码如下
//注销成功后的回调
.logoutSuccessHandler(new LogoutSuccessHandler() {
@Override
public void onLogoutSuccess(HttpServletRequest req, HttpServletResponse resp, Authentication authentication) throws IOException, ServletException {
resp.setContentType("application/json;charset=utf-8");
PrintWriter out = resp.getWriter();
out.write(new ObjectMapper().writeValueAsString(RespBean.ok("注销成功!")));
out.flush();
out.close();
}
})
SecurityConfig配置类的详细代码如下:
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
HrService hrService;
@Bean
PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
//要有一个configure方法吧hrService整进来
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(hrService);
}
//配置登录成功或者登录失败向前端传送json数据
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
//剩下的其他请求都是登录之后就能访问的
.anyRequest().authenticated()
.and()
//表单登录
.formLogin()
//修改默认登录的username
.usernameParameter("username")
//修改默认登录的password
.passwordParameter("password")
//处理表单登录的url路径
.loginProcessingUrl("/doLogin")
//默认看到的登录页面,如果是前后端分离的话,就不用配置登录页面
.loginPage("/login")
//登录成功的处理
.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();
//登录成功的hr对象
Hr hr = (Hr)authentication.getPrincipal();
hr.setPassword(null);
RespBean ok = RespBean.ok("登录成功!", hr);
//把hr写成字符串
String s = new ObjectMapper().writeValueAsString(ok);
//把字符串写出去
out.write(s);
out.flush();
out.close();
}
})
//登录失败的处理
.failureHandler(new AuthenticationFailureHandler() {
@Override
public void onAuthenticationFailure(HttpServletRequest req, HttpServletResponse resp, AuthenticationException exception) throws IOException, ServletException {
//如果登录成功就返回一段json
resp.setContentType("application/json;charset=utf-8");
//这是往出写的
PrintWriter out = resp.getWriter();
RespBean respBean = RespBean.error("登录失败!");
if(exception instanceof LockedException){
respBean.setMsg("账户被锁定,请联系管理员!");
}else if (exception instanceof CredentialsExpiredException){
respBean.setMsg("密码过期,请联系管理员!");
}else if (exception instanceof AccountExpiredException){
respBean.setMsg("账户过期,请联系管理员!");
}else if (exception instanceof DisabledException){
respBean.setMsg("账户被禁用,请联系管理员!");
}else if (exception instanceof BadCredentialsException){
respBean.setMsg("用户名或者密码输入错误,请重新输入!");
}
out.write(new ObjectMapper().writeValueAsString(respBean));
out.flush();
out.close();
}
})
//跟登录相关的接口就能直接访问
.permitAll()
.and()
.logout()
//注销成功后的回调
.logoutSuccessHandler(new LogoutSuccessHandler() {
@Override
public void onLogoutSuccess(HttpServletRequest req, HttpServletResponse resp, Authentication authentication) throws IOException, ServletException {
resp.setContentType("application/json;charset=utf-8");
PrintWriter out = resp.getWriter();
out.write(new ObjectMapper().writeValueAsString(RespBean.ok("注销成功!")));
out.flush();
out.close();
}
})
.permitAll()
.and()
//关闭csrf攻击
.csrf().disable();
}
}