SpringBoot + Spring Security 前后端分离(后端提供判断标识)
弄了一天:
- 主要是前后端分离前端接参 ,后台根据登录人所拥有的权限来给出可以访问的接口;
- 码云代码小demo,https://gitee.com/xueyuanxiang/codes/gzcy4r7219emtufh653ab43
引包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
<!-- mysql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- 工具类 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.47</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
</dependency>
Security 配置和登录逻辑
package com.kunyao.config;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import com.kunyao.interceptor.AjaxAccessDeniedHandler;
import com.kunyao.interceptor.AjaxAuthenticationEntryPoint;
import com.kunyao.interceptor.AjaxAuthenticationFailureHandler;
import com.kunyao.interceptor.AjaxAuthenticationSuccessHandler;
import com.kunyao.interceptor.MyLogoutSuccessHandle;
import com.kunyao.service.UserService;
@Configuration
@EnableWebSecurity
public class WebSecurityConfig {
@Autowired
private UserService userService;
@Autowired
private AjaxAuthenticationEntryPoint authenticationEntryPoint;//未登录 返回给前端code 为:0
@Autowired
private AjaxAuthenticationSuccessHandler authenticationSuccessHandler; // 登录成功返回给前端code 为:1
@Autowired
private AjaxAuthenticationFailureHandler authenticationFailureHandler; // 登录失败返回给前端code 为:2
@Autowired
private AjaxAccessDeniedHandler accessDeniedHandler; // 无权访问返回给前端code 为:3
@Autowired
private MyLogoutSuccessHandle logoutSuccessHandle; //注销成功 给前端code 为:4
@Bean
public UserDetailsService userDetailsService() {
return username -> {
List<com.kunyao.entity.User> users = userService.getpassWord(username);
if (users == null || users.size() == 0) {
throw new UsernameNotFoundException("用户名未找到");
}
String password = users.get(0).getPassWord();
PasswordEncoder passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
String passwordAfterEncoder = passwordEncoder.encode(password);
Map<String,List<String>> dateMap = userService.getPermissionsByRoleId(users.get(0).getRoleId());
List<String> permissions = dateMap.get("permissions");//查出登录者所有拥有的权限
String[] permissionArr = new String[permissions.size()];
int permissionArrIndex = 0;
for (String permission : permissions) {
permissionArr[permissionArrIndex] = permission;
permissionArrIndex++;
}
return User.withUsername(username).password(passwordAfterEncoder).authorities(permissionArr).build();
};
}
/**
* 注释中的拦截主要针对前后端分离
* 用这个HttpServletResponse里面的getWriter().write方法
* @return
*/
@Bean
public WebSecurityConfigurerAdapter webSecurityConfigurerAdapter() {
return new WebSecurityConfigurerAdapter() {
@Override
public void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.
authorizeRequests().antMatchers("/test/**").permitAll().//test下的链接可以被任何人访问,不作验证拦截
and().authorizeRequests().antMatchers("/admin/**").hasRole("admin").
and().authorizeRequests().antMatchers("/manager/**").hasAuthority("admin").//admin下的链接需要admin权限的人才可以访问
and().authorizeRequests().anyRequest().authenticated().// 其他 url 都需要身份认证
and().httpBasic().authenticationEntryPoint(authenticationEntryPoint).//定义认证拦截
and().exceptionHandling().accessDeniedHandler(accessDeniedHandler).//无权访问拦截
and().formLogin().
loginProcessingUrl("/security/login").//定义前端访问的接口(这里只是定义一个名字,项目是没有这个接口的,调用的还是Security自己登陆接口)
// loginPage("/test89/login1").//定义跳转的页面(前后端分离的话直接在下面拦截里给前端code)
// defaultSuccessUrl("/test89/login2").//定义成功跳转的接口
failureHandler(authenticationFailureHandler).//定义失败拦截
successHandler(authenticationSuccessHandler).//定义成功拦截
usernameParameter("username").passwordParameter("password").//可以重新定义前端传过来的用户名密码接受字段
and().logout().
logoutSuccessHandler(logoutSuccessHandle).//定义注销拦截
// logoutUrl("/logout").//定义注销接口
// logoutSuccessUrl("/logoutSuccess").//定义注销成功跳转接口
invalidateHttpSession(true).deleteCookies("cookiename").//成功注销后删除缓存中cookie
// addLogoutHandler(new MyLogoutHandle()).//上面定义过了
// and().cors().跨域的
and().csrf().disable();
}
};
}
}
拦截器且通过Security自己的登录接口返回标识给前端
没有权限
package com.yiyun.member.interceptor;
import com.alibaba.fastjson.JSON;
import com.yiyun.member.utils.JsonResult;
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 {
JsonResult result = new JsonResult("3");
result.setFlag(false);
result.setMsg("Need Authorities!");
httpServletResponse.getWriter().write(JSON.toJSONString(result));
}
}
未登录
package com.yiyun.member.interceptor;
import com.alibaba.fastjson.JSON;
import com.yiyun.member.utils.JsonResult;
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 {
JsonResult result = new JsonResult("0");
result.setFlag(true);
result.setMsg("Need Authorities!");
httpServletResponse.getWriter().write(JSON.toJSONString(result));
}
}
登录失败
package com.yiyun.member.interceptor;
import com.alibaba.fastjson.JSON;
import com.yiyun.member.utils.JsonResult;
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 {
JsonResult result = new JsonResult("2");
result.setFlag(false);
result.setMsg("用户名或密码不正确!");
httpServletResponse.setContentType("application/json;charset=UTF-8");
httpServletResponse.getWriter().write(JSON.toJSONString(result));
}
}
登录成功
package com.yiyun.member.interceptor;
import com.alibaba.fastjson.JSON;
import com.yiyun.member.utils.JsonResult;
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 {
JsonResult result = new JsonResult("1");
result.setFlag(true);
result.setMsg("Login Success!");
httpServletResponse.getWriter().write(JSON.toJSONString(result));
}
}
注销成功
package com.kunyao.interceptor;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.stereotype.Component;
import com.alibaba.fastjson.JSON;
import com.kunyao.utils.JsonResult;
@Component
public class MyLogoutSuccessHandle implements LogoutSuccessHandler {
//注销成功
@Override
public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
JsonResult result = new JsonResult();
result.setCode("4");
result.setFlag(true);
result.setMessage("LoginOut Success!");
httpServletResponse.getWriter().write(JSON.toJSONString(result));
}
}
返回给前端封装类
package com.kunyao.utils;
import java.io.Serializable;
public class JsonResult implements Serializable{
private static final long serialVersionUID = 1L;
private String code;
private String message;
private Object data;
private Boolean flag;
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public Boolean getFlag() {
return flag;
}
public void setFlag(Boolean flag) {
this.flag = flag;
}
}
查询登录人权限
package com.kunyao.service;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.transaction.Transactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.kunyao.dao.PolePermissionDao;
import com.kunyao.dao.UserDao;
import com.kunyao.entity.PolePermission;
import com.kunyao.entity.User;
@Service
@Transactional
public class UserService {
@Autowired
private UserDao userDao;
@Autowired
private PolePermissionDao polePermissionDao;
public List<User> getpassWord(String userName) {
List<User> user = userDao.findByUserName(userName);
return user;
}
public Map<String, List<String>> getPermissionsByRoleId(Integer roleId) {
Map<String,List<String>> resultMap = new HashMap<String, List<String>>();
List<PolePermission> polePermissionList = polePermissionDao.findByRoleId(roleId);
List<String> permissions = new ArrayList<String>();
for(int i =0 ;i<polePermissionList.size();i++) {
permissions.add(polePermissionList.get(i).getPermission());
}
resultMap.put("permissions", permissions);
return resultMap;
}
}
数据库建表
会员和角色 一对多的关系
demo项目结构
demo是根据这两个帖子 来做的https://blog.csdn.net/larger5/article/details/81047869
https://www.cnblogs.com/LOVE0612/p/9897647.html