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的更多相关文章

  1. springcloud +spring security多种验证方式之第三方token生成自己的token通过校验和自己的表单验证大体流程

    步骤: 1.继承 WebSecurityConfigurerAdapter.class,其中使用两个过滤器,一个spring scurity自带的UsernamePasswordAuthenticat ...

  2. SpringCloud集成Security安全(Eureka注册中心)

    1.说明 为了保护注册中心的服务安全, 避免恶意服务注册到Eureka, 需要对Eureka Server进行安全保护, 本文基于Spring Security方案, 为Eureka Server增加 ...

  3. SpringCloud集成Security安全(Config配置中心)

    1.说明 为了保护配置中心的敏感数据, 需要对Config Server进行安全保护, 本文基于Spring Security方案, 为Config Server增加最简单的Basic安全认证. 2. ...

  4. spring security 原理+实战

    疯狂创客圈 Java 高并发[ 亿级流量聊天室实战]实战系列 [博客园总入口 ] 架构师成长+面试必备之 高并发基础书籍 [Netty Zookeeper Redis 高并发实战 ] 前言 Crazy ...

  5. 学习一下 Spring Security

    一.Spring Security 1.什么是 Spring Security? (1)基本认识 Spring Security 是基于 Spring 框架,用于解决 Web 应用安全性的 一种方案, ...

  6. SpringBootSecurity学习(14)前后端分离版之 OAuth2.0介绍

    登录总结 前面基本介绍了security的常规用法,同时介绍了JWT和它的一个简单实现,基本上开发中遇到的登录问题都能解决了,即使在分布式开发,或者微服务开发中实现登录也基本没有问题了.securit ...

  7. SpringSession 独立使用

    疯狂创客圈 Java 高并发[ 亿级流量聊天室实战]实战系列 [博客园总入口 ] 架构师成长+面试必备之 高并发基础书籍 [Netty Zookeeper Redis 高并发实战 ] 前言 Crazy ...

  8. WebService安全机制的思考与实践

    近来因业务需要,需要研究webservice,于是便有这篇文章:SpringBoot整合Apache-CXF实践 一.WebService是什么? WebService是一个平台独立的.低耦合的.自包 ...

  9. SpringCloud微服务实战——搭建企业级开发框架(四十):使用Spring Security OAuth2实现单点登录(SSO)系统

    一.单点登录SSO介绍   目前每家企业或者平台都存在不止一套系统,由于历史原因每套系统采购于不同厂商,所以系统间都是相互独立的,都有自己的用户鉴权认证体系,当用户进行登录系统时,不得不记住每套系统的 ...

随机推荐

  1. 实现es6中的set和map

    转载自: https://www.cnblogs.com/hui-fly/p/9459152.html https://blog.csdn.net/roamingcode/article/detail ...

  2. 2.2 Go变量类型

    内置类型 值类型: bool 布尔类型 int(32 or 64), int8, int16, int32, int64 整数类型 uint(32 or 64), uint8(byte), uint1 ...

  3. mysql运维入门6:MySQL读写分离

    Amoeba 以MySQL为底层数据存储,并对应用提供MySQL协议接口的proxy 集中想用应用的请求,根据用户事先设置的规则,将SQL请求发送到特定的数据库上执行 基于此可以实现负载均衡.读写分离 ...

  4. TypeError: Cannot assign to read only property 'exports' of object '#<Object>'

    我的项目在mac上运行的很好,结果windows电脑,就一直报这个错误 解决方案: babel增加 @babel/plugin-transform-modules-commonjs 参考文章: htt ...

  5. 架构设计 | 接口幂等性原则,防重复提交Token管理

    本文源码:GitHub·点这里 || GitEE·点这里 一.幂等性概念 1.幂等简介 编程中一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同.就是说,一次和多次请求某一个资源会产 ...

  6. JMM_Java内存模型

    一.什么是 JMM JMM : Java 内存模型,它并不实际存在,是一种概念,一种约定! 作用 :主要是定义了 线程 与 主内存 之间存取数据的一些规则,进行一定的约束. 二.关于 JMM 的约定 ...

  7. LightOJ1336

    题目大意: 给你一个 n ,求出 1 到 n 中有多少个数的因数和为偶数. 解题思路: 可以先求出因数和为奇数的数字的个数. 由算术基本定理我们可以得到:N=P1a1P2a2P3a3 … Pnan, ...

  8. #442-Find All Duplicates in an Array-数组中重复的数字

    一.题目 给定一个整数数组 a,其中1 ≤ a[i] ≤ n (n为数组长度), 其中有些元素出现两次而其他元素出现一次. 找到所有出现两次的元素. 你可以不用到任何额外空间并在O(n)时间复杂度内解 ...

  9. 【JavaScript数据结构系列】00-开篇

    [JavaScript数据结构系列]00-开篇 码路工人 CoderMonkey 转载请注明作者与出处 ## 0. 开篇[JavaScript数据结构与算法] 大的计划,写以下两部分: 1[JavaS ...

  10. 【C++】C++数据类型

    注意:以下内容摘自文献[1],修改了部分内容. 计算机处理的对象是数据,而数据是以某种特定的形式存在的(例如整数.浮点数.字符等形式).数据结构指的是数据的组织形式.例如,数组就是一种数据结构. 1. ...