SpringCloud之Security
Spring Security是Spring提供的一个安全框架,提供认证和授权功能,最主要的是它提供了简单的使用方式,同时又有很高的灵活性,简单,灵活,强大。
我个人博客系统采用的权限框架就是Spring Security,正好整合到SpringCloud里面。
一般系统里关于角色方面通常有这么几张表,角色表、用户-角色表、菜单表、角色-菜单表等。
不过我个人博客系统主要以wordpress作为参考,沿用其12张表,如图:
一、导入Maven依赖
<properties>
<jjwt.version>0.9.</jjwt.version>
<spring-security-jwt.version>1.0..RELEASE</spring-security-jwt.version>
</properties>
<!-- springsecurity-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-jwt</artifactId>
<version>${spring-security-jwt.version}</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>${jjwt.version}</version>
</dependency>
二、编写Spring Security配置类
package com.springcloud.blog.admin.config;
import com.springcloud.blog.admin.security.UserAuthenticationProvider;
import com.springcloud.blog.admin.security.UserPermissionEvaluator;
import com.springcloud.blog.admin.security.handler.*;
import com.springcloud.blog.admin.security.jwt.JWTAuthenticationTokenFilter;
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.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
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.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler; /**
* SpringSecurity配置类
* @Author youcong
*/
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true) //开启权限注解,默认是关闭的
public class SecurityConfig extends WebSecurityConfigurerAdapter {
/**
* 自定义登录成功处理器
*/
@Autowired
private UserLoginSuccessHandler userLoginSuccessHandler;
/**
* 自定义登录失败处理器
*/
@Autowired
private UserLoginFailureHandler userLoginFailureHandler;
/**
* 自定义注销成功处理器
*/
@Autowired
private UserLogoutSuccessHandler userLogoutSuccessHandler;
/**
* 自定义暂无权限处理器
*/
@Autowired
private UserAuthAccessDeniedHandler userAuthAccessDeniedHandler;
/**
* 自定义未登录的处理器
*/
@Autowired
private UserAuthenticationEntryPointHandler userAuthenticationEntryPointHandler;
/**
* 自定义登录逻辑验证器
*/
@Autowired
private UserAuthenticationProvider userAuthenticationProvider; /**
* 加密方式
* @Author youcong
*/
@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder(){
return new BCryptPasswordEncoder();
}
/**
* 注入自定义PermissionEvaluator
*/
@Bean
public DefaultWebSecurityExpressionHandler userSecurityExpressionHandler(){
DefaultWebSecurityExpressionHandler handler = new DefaultWebSecurityExpressionHandler();
handler.setPermissionEvaluator(new UserPermissionEvaluator());
return handler;
} /**
* 配置登录验证逻辑
*/
@Override
protected void configure(AuthenticationManagerBuilder auth){
//这里可启用我们自己的登陆验证逻辑
auth.authenticationProvider(userAuthenticationProvider);
}
/**
* 配置security的控制逻辑
* @Author youcong
* @Param http 请求
*/
@Override
protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests()
// 不进行权限验证的请求或资源(从配置文件中读取)
.antMatchers(JWTConfig.antMatchers.split(",")).permitAll()
// .antMatchers("/*").permitAll()
// 其他的需要登陆后才能访问
.anyRequest().authenticated()
.and()
// 配置未登录自定义处理类
.httpBasic().authenticationEntryPoint(userAuthenticationEntryPointHandler)
.and()
// 配置登录地址
.formLogin()
.loginProcessingUrl("/login/userLogin")
// 配置登录成功自定义处理类
.successHandler(userLoginSuccessHandler)
// 配置登录失败自定义处理类
.failureHandler(userLoginFailureHandler)
.and()
// 配置登出地址
.logout()
.logoutUrl("/login/userLogout")
// 配置用户登出自定义处理类
.logoutSuccessHandler(userLogoutSuccessHandler)
.and()
// 配置没有权限自定义处理类
.exceptionHandling().accessDeniedHandler(userAuthAccessDeniedHandler)
.and()
// 开启跨域
.cors()
.and()
// 取消跨站请求伪造防护
.csrf().disable();
// 基于Token不需要session
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
// 禁用缓存
http.headers().cacheControl();
// 添加JWT过滤器
http.addFilter(new JWTAuthenticationTokenFilter(authenticationManager()));
}
}
三、编写JWTConfig和application.yml增加jwt相关配置
package com.springcloud.blog.admin.config;
import lombok.Getter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component; /**
* JWT配置类
* @Author youcong
*/
@Getter
@Component
@ConfigurationProperties(prefix = "jwt")
public class JWTConfig {
/**
* 密钥KEY
*/
public static String secret;
/**
* TokenKey
*/
public static String tokenHeader;
/**
* Token前缀字符
*/
public static String tokenPrefix;
/**
* 过期时间
*/
public static Integer expiration;
/**
* 不需要认证的接口
*/
public static String antMatchers; public void setSecret(String secret) {
this.secret = secret;
} public void setTokenHeader(String tokenHeader) {
this.tokenHeader = tokenHeader;
} public void setTokenPrefix(String tokenPrefix) {
this.tokenPrefix = tokenPrefix;
} public void setExpiration(Integer expiration) {
this.expiration = expiration * ;
} public void setAntMatchers(String antMatchers) {
this.antMatchers = antMatchers;
} }
application.yml增加如下内容:
# JWT配置
jwt:
# 密匙KEY
secret: JWTSecret
# HeaderKEY
tokenHeader: Authorization
# Token前缀字符
tokenPrefix: challenger-
# 过期时间 单位秒 1天后过期= 7天后过期=
expiration:
# 配置不需要认证的接口
antMatchers: /index/**,/login/**,/favicon.ico
# 有效时间
validTime: 7
四、编写过滤器处理类
1.UserLoginSuccessHandler.java
package com.springcloud.blog.admin.security.handler; import com.springcloud.blog.admin.config.JWTConfig;
import com.springcloud.blog.admin.security.entity.SelfUserEntity;
import com.springcloud.blog.admin.utils.AccessAddressUtil;
import com.springcloud.blog.admin.utils.JWTTokenUtil;
import com.springcloud.blog.admin.utils.RedisUtil;
import com.springcloud.blog.admin.utils.ResultUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map; /**
* @Description 登录成功处理类
* @Author youcong
*/
@Component
public class UserLoginSuccessHandler implements AuthenticationSuccessHandler { /**
* 登录成功返回结果
* @Author youcong
*/
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication){
// 组装JWT
SelfUserEntity selfUserEntity = (SelfUserEntity) authentication.getPrincipal();
String token = JWTTokenUtil.createAccessToken(selfUserEntity);
token = JWTConfig.tokenPrefix + token; // 封装返回参数
Map<String,Object> resultData = new HashMap<>();
resultData.put("code","");
resultData.put("msg", "登录成功");
resultData.put("token",token);
ResultUtil.responseJson(response,resultData);
}
}
2.UserLoginFailureHandler.java
package com.springcloud.blog.admin.security.handler; import com.springcloud.blog.admin.utils.ResultUtil;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.LockedException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; /**
* @Description 登录失败处理类
* @Author youcong
*/
@Component
public class UserLoginFailureHandler implements AuthenticationFailureHandler {
/**
* 登录失败返回结果
* @Author youcong
*/
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception){
// 这些对于操作的处理类可以根据不同异常进行不同处理
if (exception instanceof UsernameNotFoundException){
System.out.println("【登录失败】"+exception.getMessage());
ResultUtil.responseJson(response,ResultUtil.resultCode(,"用户名不存在"));
}
if (exception instanceof LockedException){
System.out.println("【登录失败】"+exception.getMessage());
ResultUtil.responseJson(response,ResultUtil.resultCode(,"用户被冻结"));
}
if (exception instanceof BadCredentialsException){
System.out.println("【登录失败】"+exception.getMessage());
ResultUtil.responseJson(response,ResultUtil.resultCode(,"密码错误"));
}
ResultUtil.responseJson(response,ResultUtil.resultCode(,"登录失败"));
}
}
3.UserLogoutSuccessHandler.java
package com.springcloud.blog.admin.security.handler; import com.springcloud.blog.admin.utils.DateUtil;
import com.springcloud.blog.admin.utils.RedisUtil;
import com.springcloud.blog.admin.utils.ResultUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map; /**
* 登出成功处理类
* @Author youcong
*/
@Component
public class UserLogoutSuccessHandler implements LogoutSuccessHandler { /**
* 用户登出返回结果
* 这里应该让前端清除掉Token
* @Author youcong
*/
@Override
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication){ Map<String,Object> resultData = new HashMap<>();
resultData.put("code","");
resultData.put("msg", "登出成功");
SecurityContextHolder.clearContext();
ResultUtil.responseJson(response,ResultUtil.resultSuccess(resultData));
}
}
4.UserAuthAccessDeniedHandler.java
package com.springcloud.blog.admin.security.handler;
import com.springcloud.blog.admin.utils.ResultUtil;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; /**
* @Description 暂无权限处理类
* @Author youcong
*/
@Component
public class UserAuthAccessDeniedHandler implements AccessDeniedHandler {
/**
* 暂无权限返回结果
* @Author youcong
*/
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException exception){
ResultUtil.responseJson(response,ResultUtil.resultCode(,"未授权"));
}
}
5.UserAuthenticationEntryPointHandler.java
package com.springcloud.blog.admin.security.handler; import com.springcloud.blog.admin.utils.ResultUtil;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; /**
* 用户未登录处理类
* @Author youcong
*/
@Component
public class UserAuthenticationEntryPointHandler implements AuthenticationEntryPoint {
/**
* 用户未登录返回结果
* @Author youcong
*/
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception){
ResultUtil.responseJson(response,ResultUtil.resultCode(,"未登录"));
}
}
6.UserAuthenticationProvider.java
自定义登录验证这个类,需要根据实际情况重写。通常来说改动不大。
package com.springcloud.blog.admin.security; import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.springcloud.blog.admin.entity.Usermeta;
import com.springcloud.blog.admin.entity.Users;
import com.springcloud.blog.admin.security.entity.SelfUserEntity;
import com.springcloud.blog.admin.service.UsermetaService;
import com.springcloud.blog.admin.service.UsersService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.LockedException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
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.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Component;
import java.util.HashSet;
import java.util.List;
import java.util.Set; /**
* 自定义登录验证
*
* @Author youcong
*/
@Component
public class UserAuthenticationProvider implements AuthenticationProvider { @Autowired
private UsersService usersService; @Autowired
private UsermetaService usermetaService; @Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
// 获取表单输入中返回的用户名
String userName = (String) authentication.getPrincipal();
// 获取表单中输入的密码
String password = (String) authentication.getCredentials();
// 查询用户是否存在
SelfUserEntity userInfo = usersService.getUserInfo(userName); if (userInfo.getUsername() == null || userInfo.getUsername() == "") {
throw new UsernameNotFoundException("用户名不存在");
} // 我们还要判断密码是否正确,这里我们的密码使用BCryptPasswordEncoder进行加密的
if (!new BCryptPasswordEncoder().matches(password, userInfo.getPassword())) {
throw new BadCredentialsException("密码不正确");
}
// 还可以加一些其他信息的判断,比如用户账号已停用等判断
if (userInfo.getStatus().equals("")) {
throw new LockedException("该用户已被冻结");
}
// 角色集合
Set<GrantedAuthority> authorities = new HashSet<>(); EntityWrapper<Usermeta> roleWrapper = new EntityWrapper<>();
roleWrapper.eq("user_id",userInfo.getUserId());
roleWrapper.eq("meta_key","wp_user_level");
// 查询用户角色
List<Usermeta> sysRoleEntityList = usermetaService.selectList(roleWrapper);
for (Usermeta sysRoleEntity: sysRoleEntityList){
authorities.add(new SimpleGrantedAuthority("ROLE_" + sysRoleEntity.getMetaValue()));
}
userInfo.setAuthorities(authorities);
// 进行登录
return new UsernamePasswordAuthenticationToken(userInfo, password, authorities);
} @Override
public boolean supports(Class<?> authentication) {
return true;
}
}
7.UserPermissionEvaluator.java
package com.springcloud.blog.admin.security; import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.springcloud.blog.admin.entity.Usermeta;
import com.springcloud.blog.admin.service.UsermetaService;
import org.apache.catalina.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.PermissionEvaluator;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component;
import java.io.Serializable;
import java.util.HashSet;
import java.util.List;
import java.util.Set; /**
* 自定义权限注解验证
* @Author youcong
*/
@Component
public class UserPermissionEvaluator implements PermissionEvaluator { @Autowired
private UsermetaService usermetaService; /**
* hasPermission鉴权方法
* 这里仅仅判断PreAuthorize注解中的权限表达式
* 实际中可以根据业务需求设计数据库通过targetUrl和permission做更复杂鉴权
* 当然targetUrl不一定是URL可以是数据Id还可以是管理员标识等,这里根据需求自行设计
* @Author youcong
* @Param authentication 用户身份(在使用hasPermission表达式时Authentication参数默认会自动带上)
* @Param targetUrl 请求路径
* @Param permission 请求路径权限
* @Return boolean 是否通过
*/
@Override
public boolean hasPermission(Authentication authentication, Object targetUrl, Object permission) {
// 获取用户信息
Usermeta selfUserEntity =(Usermeta) authentication.getPrincipal();
// 查询用户权限(这里可以将权限放入缓存中提升效率)
Set<String> permissions = new HashSet<>();
EntityWrapper<Usermeta> roleWrapper = new EntityWrapper<>();
roleWrapper.eq("user_id",selfUserEntity.getUserId());
roleWrapper.eq("meta_key","wp_user_level");
List<Usermeta> sysMenuEntityList = usermetaService.selectList(roleWrapper);
for (Usermeta sysMenuEntity:sysMenuEntityList) {
permissions.add(sysMenuEntity.getMetaValue());
}
// 权限对比
if (permissions.contains(permission.toString())){
return true;
}
return true;
}
@Override
public boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission) {
return false;
}
}
五、编写实体类
package com.springcloud.blog.admin.security.entity; import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails; import java.io.Serializable;
import java.util.Collection;
import java.util.Map; /**
* SpringSecurity用户的实体
* 注意:这里必须要实现UserDetails接口
*
* @Author youcong
*/
public class SelfUserEntity implements Serializable, UserDetails { private static final long serialVersionUID = 1L; /**
* 用户ID
*/
private Long userId;
/**
* 用户名
*/
private String username;
/**
* 密码
*/
private String password;
/**
* 状态
*/
private String status; /**
* 显示名称
*/
private String displayName; /**
* 用户参数
*/
private Map<String, String> userParamMap; /**
* 用户角色
*/
private Collection<GrantedAuthority> authorities;
/**
* 账户是否过期
*/
private boolean isAccountNonExpired = false;
/**
* 账户是否被锁定
*/
private boolean isAccountNonLocked = false;
/**
* 证书是否过期
*/
private boolean isCredentialsNonExpired = false;
/**
* 账户是否有效
*/
private boolean isEnabled = true; public static long getSerialVersionUID() {
return serialVersionUID;
} public Long getUserId() {
return userId;
} public void setUserId(Long userId) {
this.userId = userId;
} @Override
public String getUsername() {
return username;
} public void setUsername(String username) {
this.username = username;
} @Override
public String getPassword() {
return password;
} public void setPassword(String password) {
this.password = password;
} public void setAuthorities(Collection<GrantedAuthority> authorities) {
this.authorities = authorities;
} public void setEnabled(boolean enabled) {
isEnabled = enabled;
} public void setStatus(String status) {
this.status = status;
} public String getStatus() {
return status;
} public String getDisplayName() {
return displayName;
} public void setDisplayName(String displayName) {
this.displayName = displayName;
} public Map<String, String> getUserParamMap() {
return userParamMap;
} public void setUserParamMap(Map<String, String> userParamMap) {
this.userParamMap = userParamMap;
} @Override
public Collection<GrantedAuthority> getAuthorities() {
return authorities;
} @Override
public boolean isAccountNonExpired() {
return isAccountNonExpired;
} @Override
public boolean isAccountNonLocked() {
return isAccountNonLocked;
} @Override
public boolean isCredentialsNonExpired() {
return isCredentialsNonExpired;
} @Override
public boolean isEnabled() {
return isEnabled;
} }
六、编写JWT接口请求拦截器
package com.springcloud.blog.admin.security.jwt; import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.springcloud.blog.admin.config.JWTConfig;
import com.springcloud.blog.admin.security.entity.SelfUserEntity;
import com.springcloud.blog.admin.utils.CollectionUtil;
import com.springcloud.blog.admin.utils.JWTTokenUtil;
import com.springcloud.blog.admin.utils.RedisUtil;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jwts;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.util.StringUtils; import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map; /**
* JWT接口请求校验拦截器
* 请求接口时会进入这里验证Token是否合法和过期
*
* @Author youcong
*/
public class JWTAuthenticationTokenFilter extends BasicAuthenticationFilter { public JWTAuthenticationTokenFilter(AuthenticationManager authenticationManager) {
super(authenticationManager);
} @Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
// 获取请求头中JWT的Token
String tokenHeader = request.getHeader(JWTConfig.tokenHeader); if (null != tokenHeader && tokenHeader.startsWith(JWTConfig.tokenPrefix)) {
try { // 截取JWT前缀
String token = tokenHeader.replace(JWTConfig.tokenPrefix, "");
// 解析JWT
Claims claims = Jwts.parser()
.setSigningKey(JWTConfig.secret)
.parseClaimsJws(token)
.getBody();
// 获取用户名
String username = claims.getSubject();
String userId = claims.getId(); if (!StringUtils.isEmpty(username) && !StringUtils.isEmpty(userId)) {
// 获取角色
List<GrantedAuthority> authorities = new ArrayList<>();
String authority = claims.get("authorities").toString();
if (!StringUtils.isEmpty(authority)) {
List<Map<String, String>> authorityMap = JSONObject.parseObject(authority, List.class);
for (Map<String, String> role : authorityMap) {
if (!StringUtils.isEmpty(role)) {
authorities.add(new SimpleGrantedAuthority(role.get("authority")));
}
}
}
//组装参数
SelfUserEntity selfUserEntity = new SelfUserEntity();
selfUserEntity.setUsername(claims.getSubject());
selfUserEntity.setUserId(Long.parseLong(claims.getId()));
selfUserEntity.setAuthorities(authorities);
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(selfUserEntity, userId, authorities);
SecurityContextHolder.getContext().setAuthentication(authentication);
}
} catch (ExpiredJwtException e) {
System.out.println("Token过期");
} catch (Exception e) {
System.out.println("Token无效");
}
}
filterChain.doFilter(request, response);
return;
}
}
七、SpringSecurity用户的业务实现
package com.springcloud.blog.admin.security.service; import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.springcloud.blog.admin.entity.Users;
import com.springcloud.blog.admin.security.entity.SelfUserEntity;
import com.springcloud.blog.admin.service.UsersService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component; /**
* SpringSecurity用户的业务实现
*
* @Author youcong
*/
@Component
public class SelfUserDetailsService implements UserDetailsService { @Autowired
private UsersService usersService; /**
* 查询用户信息
*
* @Author youcong
* @Param username 用户名
* @Return UserDetails SpringSecurity用户信息
*/
@Override
public SelfUserEntity loadUserByUsername(String username) throws UsernameNotFoundException { EntityWrapper<Users> wrapper = new EntityWrapper<>(); //邮箱正则表达式
String expr = "^([a-zA-Z0-9_\\-\\.]+)@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.)|(([a-zA-Z0-9\\-]+\\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})$"; //是否为邮箱
if (username.matches(expr)) {
wrapper.eq("user_email", username);
} else {
wrapper.eq("user_login", username);
} // 查询用户信息
Users sysUserEntity = usersService.selectOne(wrapper);
if (sysUserEntity != null) {
// 组装参数
SelfUserEntity selfUserEntity = new SelfUserEntity();
BeanUtils.copyProperties(sysUserEntity, selfUserEntity);
return selfUserEntity;
}
return null;
}
}
八、Spring Security常用注解
1.@Secured
当@EnableGlobalMethodSecurity(securedEnabled=true)的时候,@Secured可以使用。
@PostMapping("/helloUser")
@Secured({"ROLE_normal","ROLE_admin"})
public Map<String, Object> initDashboard() {
Map<String, Object> result = new HashMap<>();
result.put(ResponseDict.RESPONSE_TITLE_KEY, "仪表盘初始化");
result.put(ResponseDict.RESPONSE_DATA_KEY, dashboardService.initDashboard());
return ResultUtil.resultSuccess(result);
}
说明:拥有normal或者admin角色的用户都可以方法helloUser()方法。另外需要注意的是这里匹配的字符串需要添加前缀“ROLE_“。
2.@PreAuthorize
Spring的 @PreAuthorize/@PostAuthorize 注解更适合方法级的安全,也支持Spring 表达式语言,提供了基于表达式的访问控制。
当@EnableGlobalMethodSecurity(prePostEnabled=true)的时候,@PreAuthorize可以使用:
@PostMapping("/initDashboard")
@PreAuthorize("hasRole('100')")
public Map<String, Object> initDashboard() {
Map<String, Object> result = new HashMap<>();
result.put(ResponseDict.RESPONSE_TITLE_KEY, "仪表盘初始化");
result.put(ResponseDict.RESPONSE_DATA_KEY, dashboardService.initDashboard());
return ResultUtil.resultSuccess(result);
}
3.@PostAuthorize
@PostAuthorize 注解使用并不多,在方法执行后再进行权限验证,适合验证带有返回值的权限,Spring EL 提供 返回对象能够在表达式语言中获取返回的对象returnObject。
当@EnableGlobalMethodSecurity(prePostEnabled=true)的时候,@PostAuthorize可以使用:
@GetMapping("/getUserInfo")
@PostAuthorize(" returnObject!=null && returnObject.username == authentication.name")
public User getUserInfo() {
Object pricipal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
User user;
if("anonymousUser".equals(pricipal)) {
user = null;
}else {
user = (User) pricipal;
}
return user;
}
九、测试
(1)登录测试,拿到token,如图:
(2)请求中如果不携带token的话,请求其它接口就会显示没有登录的提示,如图:
(3)正确的请求应当携带token,就像下面这样,如图:
(4)没有权限请求,如图:
SpringCloud之Security的更多相关文章
- springcloud +spring security多种验证方式之第三方token生成自己的token通过校验和自己的表单验证大体流程
步骤: 1.继承 WebSecurityConfigurerAdapter.class,其中使用两个过滤器,一个spring scurity自带的UsernamePasswordAuthenticat ...
- SpringCloud集成Security安全(Eureka注册中心)
1.说明 为了保护注册中心的服务安全, 避免恶意服务注册到Eureka, 需要对Eureka Server进行安全保护, 本文基于Spring Security方案, 为Eureka Server增加 ...
- SpringCloud集成Security安全(Config配置中心)
1.说明 为了保护配置中心的敏感数据, 需要对Config Server进行安全保护, 本文基于Spring Security方案, 为Config Server增加最简单的Basic安全认证. 2. ...
- spring security 原理+实战
疯狂创客圈 Java 高并发[ 亿级流量聊天室实战]实战系列 [博客园总入口 ] 架构师成长+面试必备之 高并发基础书籍 [Netty Zookeeper Redis 高并发实战 ] 前言 Crazy ...
- 学习一下 Spring Security
一.Spring Security 1.什么是 Spring Security? (1)基本认识 Spring Security 是基于 Spring 框架,用于解决 Web 应用安全性的 一种方案, ...
- SpringBootSecurity学习(14)前后端分离版之 OAuth2.0介绍
登录总结 前面基本介绍了security的常规用法,同时介绍了JWT和它的一个简单实现,基本上开发中遇到的登录问题都能解决了,即使在分布式开发,或者微服务开发中实现登录也基本没有问题了.securit ...
- SpringSession 独立使用
疯狂创客圈 Java 高并发[ 亿级流量聊天室实战]实战系列 [博客园总入口 ] 架构师成长+面试必备之 高并发基础书籍 [Netty Zookeeper Redis 高并发实战 ] 前言 Crazy ...
- WebService安全机制的思考与实践
近来因业务需要,需要研究webservice,于是便有这篇文章:SpringBoot整合Apache-CXF实践 一.WebService是什么? WebService是一个平台独立的.低耦合的.自包 ...
- SpringCloud微服务实战——搭建企业级开发框架(四十):使用Spring Security OAuth2实现单点登录(SSO)系统
一.单点登录SSO介绍 目前每家企业或者平台都存在不止一套系统,由于历史原因每套系统采购于不同厂商,所以系统间都是相互独立的,都有自己的用户鉴权认证体系,当用户进行登录系统时,不得不记住每套系统的 ...
随机推荐
- Gym101612H Hidden Supervisors
题目链接:https://vjudge.net/problem/Gym-101612H 知识点: 贪心 解题思路: 我们称除了以 \(1\) 号结点为根的树以外的树为 “其他树”. 对于每一棵树,先自 ...
- LightOJ1197
题目链接:https://vjudge.net/problem/LightOJ-1197 题目大意: 给你 a 和 b (1 ≤ a ≤ b < 231, b - a ≤ 100000),求出 ...
- SQL——SQL函数
avg(col) -- 返回数值列的平均值,NULL值不包括在计算中.count(col) -- 返回指定列的值的数目,NULL不计入:count(*)返回表中记录数:count(distinct c ...
- java数值类型之间的转换
说明:图中6个实心箭头代表转换无信息丢失,3个虚线箭头表示可能有精度损失的转换.
- linux连个文件都删除不了,什么鬼!
前言 最近不是redis 6.0 出了吗,官网介绍最新稳定版本是 6.0.3 .于是,我就准备在自己的破小服务器上安装一下.于是,出现了后续的糟心事 (linux 下的文件正常删除不了). 下载了最新 ...
- RESTful api 功能测试
0 为什么要写测试代码 代码写好了,如果能点或者能看,开发人员一般会自己点点或看看,如果没有发现问题就提交测试:更进一步,代码写好后,运行测试代码,通过后提交测试.将流程抽象下: 功能1编码-> ...
- Qt如何管理组件
转载:清凉简装的博客 解决“要继续此操作,至少需要一个有效且已启用的储存库“问题 1.在Qt安装目录找到组件管理软件MaintenanceTool,双击. 2.点击下一步,出现要继续此操作,至少需要一 ...
- 14.Java连接Redis_Jedis_主从模式
redis的主从模式之前提到过,这里我们使用redis来实现主从模式. 首先在VMware虚拟机中的Linux中打开两个终端,一个是用户jack,一个是newuser: 然后我们jack作为主机,re ...
- Android_存储之scoped storage&媒体文件
Scoped storage 文件存储介绍了内部存储和外部存储相关的内容.因为外部存储容易读写,所以在手机中经常看到很多“乱七八糟”的文件或文件夹,这些就是应用肆意创建的. Android Q(10) ...
- [Python基础]006.IO操作
IO操作 输入输出 print raw_input input 文件 打开文件 关闭文件 读文件 写文件 文件指针 实例 输入输出 输入输出方法都是Python的内建函数,并且不需要导入任何的包就可以 ...