SpringSecurity自定義註解和處理器
- 2021 年 12 月 20 日
- 筆記
登錄功能
添加一個配置類
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Resource
UserDetailsService userDetailsService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(password());
}
@Bean
PasswordEncoder password() {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin() // 表單登錄
.and().authorizeRequests()
.anyRequest()//其他請求
.authenticated();//需要認證
//關閉csrf
http.csrf().disable();
}
}
登錄的實現類
/**
* SpringSecurity 自動調用該類
* 登錄實現類 默認 繼承 UserDetailsService
*/
@Service("userDetailsService")
public class UserDetailsServiceImpl implements UserDetailsService {
@Resource
private AdminMapper adminMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
QueryWrapper<Admin> wrapper = new QueryWrapper<>();
wrapper.eq("username", username);
Admin admin = adminMapper.selectOne(wrapper);
if (admin == null) {
throw new UsernameNotFoundException("用戶名不存在");
}
List<GrantedAuthority> auths = new ArrayList<>();
return new User(admin.getUsername(), new BCryptPasswordEncoder().encode(admin.getPassword()), auths);
}
}
自定義登錄頁面
只需要修改一下配置類
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin() // 表單登錄
.loginPage("/login") //配置登錄頁面 引入了thymeleaf
.loginProcessingUrl("/user/login")//設置哪個是登錄的url
.permitAll()
.and().authorizeRequests()
.anyRequest()//其他請求
.authenticated();//需要認證
//關閉csrf
http.csrf().disable();
}
自定義認證成功或失敗狀態碼
如果你想要在認證成功或者失敗後拿到你自己定義的狀態碼,那你可以參考以下步驟
主要是下面的兩個處理器
/**
* 用戶認證失敗處理類
*/
@Component("userLoginAuthenticationFailureHandler")
public class UserLoginAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
AuthenticationException exception) throws IOException, ServletException {
System.out.println("===" + exception.getMessage());
JsonData jsonData = null;
jsonData = new JsonData(403,"用戶名或密碼錯誤");
String json = new Gson().toJson(jsonData);//包裝成Json 發送的前台
System.out.println(json);
response.setContentType("application/json;charset=utf-8");
PrintWriter out = response.getWriter();
out.write(json);
out.flush();
out.close();
}
}
/**
* 用戶認證成功處理類
*/
@Component("userLoginAuthenticationSuccessHandler")
public class UserLoginAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
JsonData jsonData = new JsonData(200,"認證OK");
String json = new Gson().toJson(jsonData);
response.setContentType("application/json;charset=utf-8");
PrintWriter out = response.getWriter();
out.write(json);
out.flush();
out.close();
}
}
在配置類中添加自己寫的兩個處理類
注入自己寫的兩個處理器
@Resource
private UserLoginAuthenticationFailureHandler userLoginAuthenticationFailureHandler;//驗證失敗的處理類
@Resource
private UserLoginAuthenticationSuccessHandler userLoginAuthenticationSuccessHandler;//驗證成功的處理類
配置上兩個處理類
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin() // 表單登錄
.loginPage("/login") //配置登錄頁面 引入了thymeleaf
.loginProcessingUrl("/user/login")//設置哪個是登錄的url
.failureHandler(userLoginAuthenticationFailureHandler)//驗證失敗處理
.successHandler(userLoginAuthenticationSuccessHandler)//驗證成功處理
.permitAll()
.and().authorizeRequests()
.anyRequest()//其他請求
.authenticated();//需要認證
//關閉csrf
http.csrf().disable();
}
此時的登錄頁面
<!DOCTYPE html>
<html lang="en" xmlns:th="//www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>登錄</title>
</head>
<body>
<section>
<span>用戶名:<input type="text" id="username" name="username"/> <br/> </span>
<span>用戶密碼:<input type="password" id="password" name="password"/> <br/> </span>
<button onclick="login()">登錄</button>
</section>
<script type="text/javascript" src="../static/js/jquery.js" th:src="@{/js/jquery.js}"></script>
<script>
function login(){
let username = document.getElementById("username");
let password = document.getElementById("password");
let username_and_password = {
username:username.value,
password:password.value
}
$.ajax({
type:"Post",
url:"/user/login",
data:username_and_password,
success:function (data) {
console.log(data)
console.log(data.code)
console.log(data.msg)
if (data.code == 200){ //拿到自己定義的狀態碼進行跳轉
alert(data.msg)
// window.location.href = "/hello";
// console.log("hehe")
}else if (data.code == 403){
alert(data.msg)
} else {
alert("客戶端出錯")
}
}
})
}
</script>
</body>
</html>
寫一個註解
標註該註解的方法或類,直接放行。
/**
* 聲明不用攔截的介面
**/
@Target({ElementType.TYPE, ElementType.METHOD}) //該註解可以用在類上或者方法上
@Retention(RetentionPolicy.RUNTIME)
public @interface NoAuthentication {
}
配置類中主要是以下這個方法
//設置哪些不需要認證
@Override
public void configure(WebSecurity web) throws Exception {
//靜態資源放行,我就隨便寫寫,根據自己靜態資源結構去寫。
String[] urls = new String[]{
"/js/**",
"/imgs/**",
"/css/**"
};
ApplicationContext applicationContext = ApplicationContextHelper.getApplicationContext();
List<String> whiteList = new ArrayList<>();
for (String url : urls) {
whiteList.add(url);
}
RequestMappingHandlerMapping requestMappingHandlerMapping = applicationContext.getBean(RequestMappingHandlerMapping.class);
// 獲取url與類和方法的對應資訊
Map<RequestMappingInfo, HandlerMethod> map = requestMappingHandlerMapping.getHandlerMethods();
for (Map.Entry<RequestMappingInfo, HandlerMethod> requestMappingInfoHandlerMethodEntry : map.entrySet()) {
RequestMappingInfo key = requestMappingInfoHandlerMethodEntry.getKey();
HandlerMethod value = requestMappingInfoHandlerMethodEntry.getValue();
Set<String> patterns = key.getPatternsCondition().getPatterns();
//無需許可權都可以訪問的類型
NoAuthentication noAuthentication = value.getBeanType().getAnnotation(NoAuthentication.class);
if (null != noAuthentication) {//整個controller不需要許可權訪問的
RequestMapping annotation = value.getBeanType().getAnnotation(RequestMapping.class);
if (null != annotation) {
String path = annotation.value()[0];
String suffix = "**";
if (path.lastIndexOf("/") != path.length() - 1)
suffix = "/**";
String s = path + suffix;
if (!whiteList.contains(s)) {
whiteList.add(s);
}
}
} else {//方法不需要許可權訪問的
NoAuthentication annotation = value.getMethod().getAnnotation(NoAuthentication.class);
if (null != annotation) {
patterns.forEach(p -> {
if (!whiteList.contains(p)) {
whiteList.add(p);
}
});
}
}
}
System.out.println("-----");
for (String s : whiteList) {
System.out.println(s);
}
urls = whiteList.toArray(urls);
super.configure(web);
web.httpFirewall(defaultHttpFirewall());
web.ignoring().antMatchers(urls);
}
/**
* 允許出現雙斜杠的URL
*
* @return
*/
@Bean
public HttpFirewall defaultHttpFirewall() {
return new DefaultHttpFirewall();
}
測試:把註解放在某個方法或者某個類上面,可以發現不用登陸也能直接進行介面的訪問
作用:如果你有一些介面是不需要認證的,比如說你去淘寶逛東西,你只是看看的話,要是讓你登陸的話就有些不合理了,這時你就可以在類似需求的類上加上該註解,就能實現不用登陸也能訪問。
歡迎
源碼://github.com/zhi-ac/security_demo
如果覺得有收穫,不妨花個幾秒鐘點個贊,歡迎關注我的公眾號玩編程地碼農,目前在寫數據結構與演算法、電腦基礎、java相關的知識。