初学springSecurity时,我们发现它还是高度前后端耦合的,为了能好好改造它,我先分析了它实现的原理:它底层是javaweb中的Filter 而Filter对象有一个dofilter方法 ,这个方法基本和sevelt的doget方法的参数一样都有 httpRequst 对象, 所以它是用的httpRequest对象进行了请求的转发. 把filter匹配的url都转发到Login里面。
所以进行改造的第一步就是改变它的底层逻辑 , 不在是转发,而是用HttpReponse对象返回 json :
package com.itheima._2021_3_2_springboot_security_devide;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.util.JSONPObject;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.devtools.restart.FailureHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
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.EnableWebSecurity;
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.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.crypto.password.Pbkdf2PasswordEncoder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
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.List;
import java.util.Map;
@SpringBootApplication(scanBasePackages = "com.itheima")
@EnableWebSecurity()
@MapperScan(basePackages = "com.itheima.Dao")
public class Application extends WebSecurityConfigurerAdapter {
@Value("${system.user.password.secret}")
private String scret;
@Bean("passwordEncoder")
public PasswordEncoder getPasswordEncoder(){
return new Pbkdf2PasswordEncoder();
}
@Autowired
@Qualifier("passwordEncoder")
private PasswordEncoder PasswordEncoder;
@Autowired
@Qualifier("Service1")
UserDetailsService userDetailsService;
@Autowired
private ObjectMapper objectMapper;
// @Override
// public void configure(AuthenticationManagerBuilder authenticationManagerBuilder) {
// PasswordEncoder passwordEncoder = new Pbkdf2PasswordEncoder(scret);
//
// try {
// authenticationManagerBuilder.userDetailsService(userDetailsService)
// .passwordEncoder(passwordEncoder);
// } catch (Exception e) {
// e.printStackTrace();
// }
//
//
// }
@Bean
public AuthenticationProvider authenticationProvider(){
DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
authenticationProvider.setPasswordEncoder(PasswordEncoder);
authenticationProvider.setUserDetailsService(userDetailsService);
return authenticationProvider;
}
@Override
public void configure(HttpSecurity http) throws Exception {
http
.authenticationProvider(authenticationProvider())
.httpBasic()
//未登录时,进行json格式的提示,很喜欢这种写法,不用单独写一个又一个的类
.authenticationEntryPoint(new AuthenticationEntryPoint() {
@Override
public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
//在这里 可以把我们 覆写的filter写入 //这个filter处理的是未登录的问题
httpServletResponse.setContentType("text/html;charset=UTF-8");
PrintWriter writer = httpServletResponse.getWriter();
Map<String, Object> map = new HashMap<String, Object>();
map.put("code", 403);
map.put("message", "未登录");
writer.write(objectMapper.writeValueAsString(map));
writer.flush();
writer.close();
}
})
//已经登录失败的处理
.and()
.authorizeRequests()
.anyRequest().authenticated()//必须授权了才能范围
.and()
.formLogin()
.permitAll()
//登录失败 的handler ,也是返回json而不是转发
.failureHandler(new AuthenticationFailureHandler() {
@Override
public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
httpServletResponse.setContentType("application/json;charset=utf-8");
httpServletResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
PrintWriter out = httpServletResponse.getWriter();
Map<String, Object> map = new HashMap<String, Object>();
map.put("code", 401);
if (e instanceof BadCredentialsException) {
map.put("message", "用户名或密码错误");
} else if (e instanceof DisabledException) {
map.put("message", "账户被禁用");
} else {
map.put("message", "登录失败!");
}
out.write(objectMapper.writeValueAsString(map));
out.flush();
out.close();
}
})
//登录成功时
.successHandler(new AuthenticationSuccessHandler() {
@Override
public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
Map<String, Object> map = new HashMap<String, Object>();
map.put("code", 200);
map.put("message", "登录成功");
map.put("data", authentication);
httpServletResponse.setContentType("application/json;charset=utf-8");
PrintWriter out = httpServletResponse.getWriter();
out.write(objectMapper.writeValueAsString(map));
out.flush();
out.close();
}
})
.and()
.exceptionHandling()
//当没有权限访问某url时,
.accessDeniedHandler(new AccessDeniedHandler() {
@Override
public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {
httpServletResponse.setContentType("application/json;charset=utf-8");
httpServletResponse.setStatus(HttpServletResponse.SC_FORBIDDEN);
PrintWriter out = httpServletResponse.getWriter();
Map<String, Object> map = new HashMap<String, Object>();
map.put("code", 403);
map.put("message", "权限不足");
out.write(objectMapper.writeValueAsString(map));
out.flush();
out.close();
}
})
.and()
.logout()
.logoutSuccessHandler(new LogoutSuccessHandler() {
@Override
public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
Map<String, Object> map = new HashMap<String, Object>();
map.put("code", 200);
map.put("message", "退出成功");
map.put("data", authentication);
httpServletResponse.setContentType("application/json;charset=utf-8");
PrintWriter out = httpServletResponse.getWriter();
out.write(objectMapper.writeValueAsString(map));
out.flush();
out.close();
}
}).permitAll();
//开启跨域访问
http.cors().disable();
//开启模拟请求
http.csrf().disable(); //其实csrf也可以用前后端分离来实现 ,
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
测试:用本地html调用接口试试
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta http-equiv="content-type" content="text/html;charset=utf-8">
</head>
<body>
<form name='f' action='http://localhost:8080/login' method='POST'>
<table>
<tr><td>User:</td><td><input type='text' name='username' value=''></td></tr>
<tr><td>Password:</td><td><input type='password' name='password'/></td></tr>
<tr><td colspan='2'><input name="submit" type="submit" value="Login"/></td></tr>
</table>
</form>
<a href="www.baidu.com">???</a>
</body>
</html>
得到json提示
{“code”:401,“message”:“用户名或密码错误”}
到这里springSecurity的前后分离 也就完成了,总结,传统javawe工程转前后端分离项目基本就是把请求的转发和重定向 ,变成返回json字符串。