一、前言


之前在团队里边做的项目的基于 session 的登录拦截,属于后端全栈式的开发的模式:
全栈式使用 SpringBoot + SpringSecurity 做登录认证
<https://blog.csdn.net/larger5/article/details/79439478>

而公司这边都是前后端分离鲜明的,前端不要接触过多的业务逻辑,都由后端解决,基本思路是这样的:
服务端通过 JSON字符串,告诉前端用户有没有登录、认证,前端根据这些提示跳转对应的登录页、认证页等。

二、代码

代码已经放在github 上了:https://github.com/larger5/SpringBoot_SpringSecurity.git
<https://github.com/larger5/SpringBoot_SpringSecurity.git>
温馨提示:这些代码都是通用的~~


下面给个示例,该自上述的之前的代码

1.AjaxResponseBody 给前端JSON的格式

返回给前端的数据格式
package com.cun.security3.bean; import java.io.Serializable; public class
AjaxResponseBody implements Serializable{ private String status; private String
msg;private Object result; private String jwtToken; public String getStatus() {
return status; } public void setStatus(String status) { this.status = status; }
public String getMsg() { return msg; } public void setMsg(String msg) { this
.msg = msg; }public Object getResult() { return result; } public void setResult
(Object result) {this.result = result; } public String getJwtToken() { return
jwtToken; }public void setJwtToken(String jwtToken) { this.jwtToken = jwtToken;
} }
2.AjaxAuthenticationEntryPoint 未登录

用户没有登录时返回给前端的数据
package com.cun.security3.config; import com.alibaba.fastjson.JSON; import
com.cun.security3.bean.AjaxResponseBody;import
org.springframework.security.core.AuthenticationException;import
org.springframework.security.web.AuthenticationEntryPoint;import
org.springframework.stereotype.Component;import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest; import
javax.servlet.http.HttpServletResponse;import java.io.IOException; @Component
public class AjaxAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override public void commence(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse, AuthenticationException e)throws
IOException, ServletException { AjaxResponseBody responseBody =new
AjaxResponseBody(); responseBody.setStatus("000"); responseBody.setMsg("Need
Authorities!");
httpServletResponse.getWriter().write(JSON.toJSONString(responseBody)); } }
3.AjaxAuthenticationFailureHandler 登录失败

用户登录失败时返回给前端的数据
package com.cun.security3.config; import com.alibaba.fastjson.JSON; import
com.cun.security3.bean.AjaxResponseBody;import
org.springframework.security.core.AuthenticationException;import
org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.stereotype.Component; import
javax.servlet.ServletException;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import java.io.IOException;
@Component public class AjaxAuthenticationFailureHandler implements
AuthenticationFailureHandler { @Override public void onAuthenticationFailure
(HttpServletRequest httpServletRequest, HttpServletResponse
httpServletResponse, AuthenticationException e)throws IOException,
ServletException { AjaxResponseBody responseBody =new AjaxResponseBody();
responseBody.setStatus("400"); responseBody.setMsg("Login Failure!");
httpServletResponse.getWriter().write(JSON.toJSONString(responseBody)); } }
4.AjaxAuthenticationSuccessHandler 登录成功

用户登录成功时返回给前端的数据
package com.cun.security3.config; import com.alibaba.fastjson.JSON; import
com.cun.security3.bean.AjaxResponseBody;import
org.springframework.security.core.Authentication;import
org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component; import
javax.servlet.ServletException;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import java.io.IOException;
@Component public class AjaxAuthenticationSuccessHandler implements
AuthenticationSuccessHandler { @Override public void onAuthenticationSuccess
(HttpServletRequest httpServletRequest, HttpServletResponse
httpServletResponse, Authentication authentication)throws IOException,
ServletException { AjaxResponseBody responseBody =new AjaxResponseBody();
responseBody.setStatus("200"); responseBody.setMsg("Login Success!");
httpServletResponse.getWriter().write(JSON.toJSONString(responseBody)); } }
5.AjaxAccessDeniedHandler 无权访问
package com.cun.security3.config; import com.alibaba.fastjson.JSON; import
com.cun.security3.bean.AjaxResponseBody;import
org.springframework.security.access.AccessDeniedException;import
org.springframework.security.web.access.AccessDeniedHandler;import
org.springframework.stereotype.Component;import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest; import
javax.servlet.http.HttpServletResponse;import java.io.IOException; @Component
public class AjaxAccessDeniedHandler implements AccessDeniedHandler { @Override
public void handle(HttpServletRequest httpServletRequest, HttpServletResponse
httpServletResponse, AccessDeniedException e)throws IOException,
ServletException { AjaxResponseBody responseBody =new AjaxResponseBody();
responseBody.setStatus("300"); responseBody.setMsg("Need Authorities!");
httpServletResponse.getWriter().write(JSON.toJSONString(responseBody)); } }
6.SpringSecurityConf 登录拦截全局配置
package com.cun.security3.config; import
org.springframework.beans.factory.annotation.Autowired;import
org.springframework.context.annotation.Configuration;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.config.http.SessionCreationPolicy;
@Configuration public class SpringSecurityConf extends
WebSecurityConfigurerAdapter { @Autowired AjaxAuthenticationEntryPoint
authenticationEntryPoint;// 未登陆时返回 JSON 格式的数据给前端(否则为 html) @Autowired
AjaxAuthenticationSuccessHandler authenticationSuccessHandler;// 登录成功返回的 JSON
格式数据给前端(否则为 html) @Autowired AjaxAuthenticationFailureHandler
authenticationFailureHandler;// 登录失败返回的 JSON 格式数据给前端(否则为 html) @Autowired
AjaxLogoutSuccessHandler logoutSuccessHandler;// 注销成功返回的 JSON 格式数据给前端(否则为 登录时的
html) @Autowired AjaxAccessDeniedHandler accessDeniedHandler; // 无权访问返回的 JSON
格式数据给前端(否则为 403 html 页面) @Autowired SelfAuthenticationProvider provider; //
自定义安全认证 @Override protected void configure(AuthenticationManagerBuilder auth)
throws Exception { // 加入自定义的安全认证 auth.authenticationProvider(provider); }
@Override protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.httpBasic().authenticationEntryPoint(authenticationEntryPoint) .and()
.authorizeRequests() .anyRequest() .authenticated()// 其他 url 需要身份认证 .and()
.formLogin()//开启登录 .successHandler(authenticationSuccessHandler) // 登录成功
.failureHandler(authenticationFailureHandler)// 登录失败 .permitAll() .and()
.logout() .logoutSuccessHandler(logoutSuccessHandler) .permitAll();
http.exceptionHandling().accessDeniedHandler(accessDeniedHandler);// 无权访问 JSON
格式的数据 } }
7.SelfUserDetails 自定义 user 对象
package com.cun.security3.config; import
org.springframework.security.core.GrantedAuthority;import
org.springframework.security.core.userdetails.UserDetails;import
java.io.Serializable;import java.util.Collection; import java.util.Set; /** * ①
定义 user 对象 */ public class SelfUserDetails implements UserDetails, Serializable
{ private String username; private String password; private Set<? extends
GrantedAuthority> authorities;@Override public Collection<? extends
GrantedAuthority>getAuthorities() { return this.authorities; } public void
setAuthorities(Set<? extends GrantedAuthority> authorities) { this.authorities
= authorities; }@Override public String getPassword() { // 最重点Ⅰ return this
.password; }@Override public String getUsername() { // 最重点Ⅱ return this
.username; }public void setUsername(String username) { this.username =
username; }public void setPassword(String password) { this.password = password;
}@Override public boolean isAccountNonExpired() { return true; } @Override
public boolean isAccountNonLocked() { return true; } @Override public boolean
isCredentialsNonExpired() { return true; } @Override public boolean isEnabled()
{return true; } }
8.SelfUserDetailsService 用户认证、权限
package com.cun.security3.config; import
org.springframework.security.authentication.encoding.Md5PasswordEncoder;import
org.springframework.security.core.GrantedAuthority;import
org.springframework.security.core.authority.SimpleGrantedAuthority;import
org.springframework.security.core.userdetails.UserDetails;import
org.springframework.security.core.userdetails.UserDetailsService;import
org.springframework.security.core.userdetails.UsernameNotFoundException;import
org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;import
org.springframework.stereotype.Component;import java.util.HashSet; import
java.util.Set;/** * ② 根据 username 获取数据库 user 信息 */ @Component public class
SelfUserDetailsService implements UserDetailsService { @Override public
UserDetailsloadUserByUsername(String username) throws UsernameNotFoundException
{//构建用户信息的逻辑(取数据库/LDAP等用户信息) SelfUserDetails userInfo = new SelfUserDetails();
userInfo.setUsername(username);// 任意用户名登录 Md5PasswordEncoder md5PasswordEncoder
=new Md5PasswordEncoder(); String encodePassword =
md5PasswordEncoder.encodePassword("123", username); // 模拟从数据库中获取的密码原为 123
userInfo.setPassword(encodePassword); Set authoritiesSet =new HashSet();
GrantedAuthority authority =new SimpleGrantedAuthority("ROLE_ADMIN"); //
模拟从数据库中获取用户角色 authoritiesSet.add(authority);
userInfo.setAuthorities(authoritiesSet);return userInfo; } }
9.SelfAuthenticationProvider 前端交互
package com.cun.security3.config; import
org.springframework.beans.factory.annotation.Autowired;import
org.springframework.security.authentication.AuthenticationProvider;import
org.springframework.security.authentication.BadCredentialsException;import
org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.encoding.Md5PasswordEncoder;
import org.springframework.security.core.Authentication; import
org.springframework.security.core.AuthenticationException;import
org.springframework.security.core.GrantedAuthority;import
org.springframework.security.core.authority.SimpleGrantedAuthority;import
org.springframework.security.core.userdetails.UserDetails;import
org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;import
org.springframework.stereotype.Component;import java.util.HashSet; import
java.util.Set;@Component public class SelfAuthenticationProvider implements
AuthenticationProvider { @Autowired SelfUserDetailsService userDetailsService;
@Override public Authentication authenticate(Authentication authentication)
throws AuthenticationException { String userName = (String)
authentication.getPrincipal();// 这个获取表单输入中返回的用户名; String password = (String)
authentication.getCredentials();// 这个是表单中输入的密码; Md5PasswordEncoder
md5PasswordEncoder =new Md5PasswordEncoder(); String encodePwd =
md5PasswordEncoder.encodePassword(password, userName); UserDetails userInfo =
userDetailsService.loadUserByUsername(userName);if
(!userInfo.getPassword().equals(encodePwd)) {throw new BadCredentialsException(
"用户名密码不正确,请重新登陆!"); } return new UsernamePasswordAuthenticationToken(userName,
password, userInfo.getAuthorities()); }@Override public boolean supports
(Class<?> authentication) {return true; } }
三、使用 postman 测试 ajax

1.未登录



2.登录失败



3.登录成功



4.注销成功



四、其他

温馨提示:session+cookie 不安全 ~
上述略有前后端分离的影子,真正前后端开发还要引入 JWT,有空再叙


后来还是写好了:
2018.7.18 更新:前后端分离 SpringBoot + SpringSecurity + JWT + RBAC 实现用户无状态请求验证
<https://blog.csdn.net/larger5/article/details/81063438>

友情链接
KaDraw流程图
API参考文档
OK工具箱
云服务器优惠
阿里云优惠券
腾讯云优惠券
华为云优惠券
站点信息
问题反馈
邮箱:ixiaoyang8@qq.com
QQ群:637538335
关注微信