在Spring Boot中使用Spring Security
一、引入依赖
我是利用maven来构建项目的,因此需要在maven中引入Spring Security的以来,在pom.xml中引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
二、自定义Spring Security配置
1. 返回给前端的格式
返回的result都以json的格式给前端
Result.java
import java.io.Serializable;
/**
* @Auther:kiritoghy
* @Desc:用于返回对应消息
* @Date:19-7-23 下午2:05
*/
public class Result implements Serializable {
int code;
String message;
Object data;
public Result() {
}
public Result(int code, String message) {
this.code = code;
this.message = message;
}
public int getCode() {
return code;
}
public void setCode(int 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;
}
@Override
public String toString() {
return "Result{" +
"code=" + code +
", message='" + message + '\'' +
", data=" + data +
'}';
}
}
ResultGenerator.java
import org.omg.PortableInterceptor.SUCCESSFUL;
/**
* @Auther:kiritoghy
* @Date:19-7-23 下午6:00
*/
public class ResultGenerator {
private static final int SUCCESS_CODE = 200;
private static int FAIL_CODE = 500;
private static final String DEFAULT_SUCCESS_MESSAGE = "SUCCESS";
private static final String DEFAULT_FAIL_MESSAGE = "FAIL";
/**
* 生成默认成功信息
* @return
*/
public static Result genSuccessResult(){
Result result = new Result();
result.setCode(SUCCESS_CODE);
result.setMessage(DEFAULT_SUCCESS_MESSAGE);
return result;
}
/**
* 生成指定成功信息
* @param message
* @return
*/
public static Result genSuccessResult(String message){
Result result = new Result();
result.setCode(SUCCESS_CODE);
result.setMessage(message);
return result;
}
/**
* 生成带有返回数据的成功信息
* @param data
* @return
*/
public static Result genSuccessResult(Object data){
Result result = new Result();
result.setCode(SUCCESS_CODE);
result.setMessage(DEFAULT_SUCCESS_MESSAGE);
result.setData(data);
return result;
}
/**
* 生成默认失败信息
* @return
*/
public static Result genFailResult(){
Result result = new Result();
result.setCode(FAIL_CODE);
result.setMessage(DEFAULT_FAIL_MESSAGE);
return result;
}
/**
* 生成指定失败信息
* @param message
* @return
*/
public static Result genFailResult(String message){
Result result = new Result();
result.setCode(FAIL_CODE);
result.setMessage(message);
return result;
}
/**
* 生成带有数据的失败信息
* @param data
* @return
*/
public static Result genFailResult(Object data){
Result result = new Result();
result.setCode(FAIL_CODE);
result.setMessage(DEFAULT_FAIL_MESSAGE);
result.setData(data);
return result;
}
}
2. 用户自定义user
SelfUserDetails继承了UserDetails接口,用来认证用户的信息
SelfUserDetails.java
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
import java.util.Set;
/**
* @Auther: kiritoghy
* @Date: 19-7-23 下午7:20
*/
public class SelfUserDetails implements UserDetails {
private Long id;
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;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}
3. SelfUserServiceImpl
SelfUserDetailsService继承了UserDetailsService,实现loadUserByUsername方法
SelfUserServiceImpl.java
import com.uestc.labelproject.dao.UserMapper;
import com.uestc.labelproject.entity.SelfUserDetails;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.BadCredentialsException;
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.stereotype.Component;
import java.util.HashSet;
import java.util.Set;
/**
* @Auther: kiritoghy
* @Date: 19-7-23 下午7:24
*/
@Component
public class SelfUserServiceImpl implements UserDetailsService {
@Autowired
UserMapper userMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
SelfUserDetails user = userMapper.getUser(username);
if(user == null){
throw new BadCredentialsException("该用户不存在");
}
String authorities = userMapper.getAuthoritiesById(user.getId());
Set authoritiesSet = new HashSet();
GrantedAuthority authority = new SimpleGrantedAuthority(authorities);
authoritiesSet.add(authority);
user.setAuthorities(authoritiesSet);
return user;
}
}
该方法从数据库获取到user信息,放入SelfUserDetails中
4. CustomAuthenticationEntryPoint
CustomAuthenticationEntryPoint实现AuthenticationEntryPoint接口,处理用户未登录
CustomAuthenticationEntryPoint.java
import com.alibaba.fastjson.JSON;
import com.uestc.labelproject.utils.ResultGenerator;
import lombok.extern.slf4j.Slf4j;
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;
/**
* @Auther: kiritoghy
* @Desc:入口处理,用于未登录等
* @Date: 19-7-23 下午6:50
*/
@Component
@Slf4j
public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
log.info("This is CustomAuthenticationEntryPoint");
httpServletResponse.setContentType("application/json;charset=utf-8");
httpServletResponse.getWriter().write(JSON.toJSONString(ResultGenerator.genFailResult("用户未认证")));
}
}
5. CustomAuthenticationSuccessHandler
CustomAuthenticationSuccessHandler.java
实现AuthenticationSuccessHandler接口,处理登录成功的情况
import com.alibaba.fastjson.JSON;
import com.uestc.labelproject.entity.SelfUserDetails;
import com.uestc.labelproject.entity.User;
import com.uestc.labelproject.service.AdminUserService;
import com.uestc.labelproject.utils.AccessAddressUtil;
import com.uestc.labelproject.utils.JwtTokenUtil;
import com.uestc.labelproject.utils.RedisUtil;
import com.uestc.labelproject.utils.ResultGenerator;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
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;
import java.util.HashMap;
import java.util.Map;
/**
* @Auther: kiritoghy
* @Desc:登录成功处理器
* @Date: 19-7-23 下午6:56
*/
@Slf4j
@Component
public class CustomAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
@Value("${token.expirationSeconds}")
private int expirationSeconds;
@Value("${token.validTime}")
private int validTime;
@Autowired
RedisUtil redisUtil;
@Autowired
AdminUserService adminUserService;
@Override
public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
String ip = AccessAddressUtil.getIpAddress(httpServletRequest);
Map<String,Object> map = new HashMap<>();
map.put("ip",ip);
SelfUserDetails selfUserDetails = (SelfUserDetails) authentication.getPrincipal();
String token = JwtTokenUtil.generateToken(selfUserDetails.getUsername(), expirationSeconds, map);
Integer expire = validTime*24*60*60*1000;
User user = adminUserService.getUserById(selfUserDetails.getId());
user.setPassword(null);
String currentIp = AccessAddressUtil.getIpAddress(httpServletRequest);
redisUtil.setTokenRefresh(token,selfUserDetails.getUsername(),currentIp);
log.info("用户{}登录成功,信息已保存至redis",selfUserDetails.getUsername());
Map<String,Object> res = new HashMap<>();
res.put("token", token);
res.put("user", user);
httpServletResponse.setContentType("application/json;charset=utf-8");
httpServletResponse.getWriter().write(JSON.toJSONString(ResultGenerator.genSuccessResult(res)));
}
}
6. CustomAuthenticationFailureHandler
实现AuthenticationFailureHandler接口,处理用户登录失败
CustomAuthenticationFailureHandler.java
import com.alibaba.fastjson.JSON;
import com.uestc.labelproject.utils.ResultGenerator;
import lombok.extern.slf4j.Slf4j;
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;
/**
* @Auther: kiritoghy
* @Desc:登录失败处理器
* @Date: 19-7-23 下午7:57
*/
@Slf4j
@Component
public class CustomAuthenticationFailureHandler implements AuthenticationFailureHandler {
@Override
public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
log.info("Try login Failed");
httpServletResponse.setContentType("application/json;charset=utf-8");
httpServletResponse.getWriter().write(JSON.toJSONString(ResultGenerator.genFailResult()));
}
}
7. CustomAccessDeniedHandler
实现AccessDeniedHandler接口,处理无权登录的情况
CustomAccessDeniedHandler.java
import com.alibaba.fastjson.JSON;
import com.uestc.labelproject.utils.ResultGenerator;
import lombok.extern.slf4j.Slf4j;
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;
/**
* @Auther: kiritoghy
* @Desc:权限不足处理
* @Date: 19-7-23 下午9:59
*/
@Component
@Slf4j
public class CustomAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {
log.info("用户权限不够");
httpServletResponse.setContentType("application/json;charset=utf-8");
httpServletResponse.getWriter().write(JSON.toJSONString(ResultGenerator.genFailResult("用户权限不足")));
}
}
### 8. CustomLogoutSuccessHandler
实现LogoutSuccessHandler接口,处理退出成功
#### CustomLogoutSuccessHandler.java
```java
import com.alibaba.fastjson.JSON;
import com.uestc.labelproject.utils.DateUtil;
import com.uestc.labelproject.utils.RedisUtil;
import com.uestc.labelproject.utils.ResultGenerator;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @Auther: kiritoghy
* @Desc:登出
* @Date: 19-7-23 下午8:03
*/
@Slf4j
@Component
public class CustomLogoutSuccessHandler implements LogoutSuccessHandler {
@Autowired
RedisUtil redisUtil;
@Override
public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
String authHeader = httpServletRequest.getHeader("Authorization");
if (authHeader != null && authHeader.startsWith("Bearer ")) {
final String authToken = authHeader.substring("Bearer ".length());
//将token放入黑名单中
redisUtil.hset("blacklist", authToken, DateUtil.getTime());
log.info("用户登出成功!token:{}已加入redis黑名单",authToken);
}
httpServletResponse.setContentType("application/json;charset=utf-8");
httpServletResponse.getWriter().write(JSON.toJSONString(ResultGenerator.genSuccessResult("Logout Success!")));
}
}
9. CustomAuthenticationProvider
实现AuthenticationProvider接口,用户登录判断,可用于自定义登录
CustomAuthenticationProvider.java
import com.uestc.labelproject.entity.SelfUserDetails;
import com.uestc.labelproject.service.impl.SelfUserServiceImpl;
import lombok.extern.slf4j.Slf4j;
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.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Component;
/**
* @Auther: kiritoghy
* @Desc:用户登录判断,可用于自定义登录方式
* @Date: 19-7-23 下午9:27
*/
@Component
@Slf4j
public class CustomAuthenticationProvider implements AuthenticationProvider {
@Autowired
SelfUserServiceImpl selfUserService;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String username = authentication.getName();
String password = (String)authentication.getCredentials();
SelfUserDetails selfUserDetails = (SelfUserDetails) selfUserService.loadUserByUsername(username);
log.info("用户请求的账户密码:{}/{}",username,password);
log.info("数据库的密码:{}",selfUserDetails.getPassword());
if(!password.equals(selfUserDetails.getPassword())){
throw new BadCredentialsException("密码不正确");
}
log.info("用户权限:{}",selfUserDetails.getAuthorities());
return new UsernamePasswordAuthenticationToken(selfUserDetails,password,selfUserDetails.getAuthorities());
}
@Override
public boolean supports(Class<?> aClass) {
return aClass.equals(UsernamePasswordAuthenticationToken.class);
}
}
以上为基本的自定义配置文件,其中关于jwt token的部分以及redis的部分下次再总结
Comments | 0 条评论