简介
目前市场主流的安全控制框架主要分为Shiro\Security\CAS等
Apache Shiro是Java的一个安全框架。功能强大,使用简单的Java安全框架,它为开发人员提供一个直观而全面的认证,授权,加密及会话管理的解决方案。
实际上,Shiro的主要功能是管理应用程序中与安全相关的全部,同时尽可能支持多种实现方法。Shiro是建立在完善的接口驱动设计和面向对象原则之上的,支持各种自定义行为。Shiro提供的默认实现,使其能完成与其他安全框架同样的功能,这不也是我们一直努力想要得到的吗!
Apache Shiro相当简单,对比Spring Security,可能没有Spring Security做的功能强大,但是在实际工作时可能并不需要那么复杂的东西,所以使用小而简单的Shiro就足够了。对于它俩到底哪个好,这个不必纠结,能更简单的解决项目问题就好了。
本篇文章主要给大家介绍一下spring security,话不多说开始整干货
spring security
的核心功能主要包括:
- 认证 (你是谁)
- 授权 (你能干什么)
- 攻击防护 (防止伪造身份)
这一思想与shiro
是一致的,所以可见安全框架基本的设计思路是一致的,即告诉我你是谁?你能干什么?
其核心就是一组过滤器链,项目启动后将会自动配置。最核心的就是 Basic Authentication Filter
用来认证用户的身份,一个在spring security
中一种过滤器处理一种认证方式。
比如,对于username password认证过滤器来说,
会检查是否是一个登录请求
是否包含username 和 password (也就是该过滤器需要的一些认证信息)
如果不满足则放行给下一个
下一个按照自身职责判定是否是自身需要的信息,basic的特征就是在请求头中有 Authorization:Basic eHh4Onh4 的信息。中间可能还有更多的认证过滤器。最后一环是 FilterSecurityInterceptor,这里会判定该请求是否能进行访问rest服务,判断的依据是 SecurityConfig中的配置,如果被拒绝了就会抛出不同的异常(根据具体的原因)。Exception Translation Filter 会捕获抛出的错误,然后根据不同的认证方式进行信息的返回提示。
注意:绿色的过滤器可以配置是否生效,其他的都不能控制。
开始代码
1) 引入依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<!--Security依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- MybatisPlus 核心库 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.5</version>
</dependency>
<!-- JSON工具 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.45</version>
</dependency>
<!-- JWT依赖 -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-jwt</artifactId>
<version>1.0.9.RELEASE</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.0</version>
</dependency>
</dependencies>
2) 配置文件
server:
port: 8086
spring:
datasource:
username: root
password: 123456
url: jdbc:mysql://localhost:3306/netty?characterEncoding=utf-8&useSSl=false&serverTimezone=GMT%2B8&autoReconnect=true&useSSL=false
driver-class-name: com.mysql.jdbc.Driver
# JWT配置
jwt:
# 密匙KEY
secret: JWTSecret
# HeaderKEY
tokenHeader: Authorization
# Token前缀字符
tokenPrefix: Sans-
# 过期时间 单位秒 1天后过期=86400 7天后过期=604800
expiration: 300
# 配置不需要认证的接口
antMatchers: /index/**,/login/**,/favicon.ico
mybatis-plus:
# xml扫描,多个目录用逗号或者分号分隔(告诉 Mapper 所对应的 XML 文件位置)
mapper-locations: classpath:mapper/*.xml
type-aliases-package: com.sbs.security
# global-config:
# db-config:
# #主键类型 AUTO:"数据库ID自增" INPUT:"用户输入ID",ID_WORKER:"全局唯一ID (数字类型唯一ID)", UUID:"全局唯一ID UUID";
# id-type: AUTO
# #字段策略 IGNORED:"忽略判断" NOT_NULL:"非 NULL 判断") NOT_EMPTY:"非空判断"
# field-strategy: NOT_EMPTY
# #数据库类型
# db-type: MYSQL
configuration:
# 是否开启自动驼峰命名规则映射:从数据库列名到Java属性驼峰命名的类似映射
map-underscore-to-camel-case: true
# 返回map时true:当查询数据为空时字段返回为null,false:不加这个查询数据为空时,字段将被隐藏
call-setters-on-nulls: true
# 这个配置会将执行的sql打印出来,在开发或测试的时候可以用
# log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
3) 搭建项目整体结构
4) 编码
4.1)SelfUserEntity.java安全登录对象
@Data
public class SelfUserEntity implements Serializable, UserDetails {
private static final long serialVersionUID = 1L;
/**
* 用户ID
*/
private Long userId;
/**
* 用户名
*/
private String username;
/**
* 密码
*/
private String password;
/**
* 状态:NORMAL正常 PROHIBIT禁用
*/
private String status;
/**
* 用户角色
*/
private Collection<GrantedAuthority> authorities;
/**
* 账户是否过期
*/
private boolean isAccountNonExpired = false;
/**
* 账户是否被锁定
*/
private boolean isAccountNonLocked = false;
/**
* 证书是否过期
*/
private boolean isCredentialsNonExpired = false;
/**
* 账户是否有效
*/
private boolean isEnabled = true;
@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;
}
4.2) SelfUserDetailsService.java用与获取用户信息
@Component
public class SelfUserDetailsService implements UserDetailsService {
@Autowired
private SysUserService sysUserService;
/**
* 查询用户信息
* @Author Sans
* @CreateTime 2019/9/13 17:23
* @Param username 用户名
* @Return UserDetails SpringSecurity用户信息
*/
@Override
public SelfUserEntity loadUserByUsername(String username) throws UsernameNotFoundException {
// 查询用户信息
SysUserEntity sysUserEntity =sysUserService.selectUserByName(username);
if (sysUserEntity!=null){
// 组装参数
SelfUserEntity selfUserEntity = new SelfUserEntity();
BeanUtils.copyProperties(sysUserEntity,selfUserEntity);
return selfUserEntity;
}
return null;
}
}
4.3) JWTAuthenticationTokenFilter.java唯一的登录拦截器
@Slf4j
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){
log.info("Token过期");
} catch (Exception e) {
log.info("Token无效");
}
}
filterChain.doFilter(request, response);
return;
}
}
4.4) UserAuthAccessDeniedHandler.java无权限处理类
@Component
public class UserAuthAccessDeniedHandler implements AccessDeniedHandler{
/**
* 暂无权限返回结果
* @Author Sans
* @CreateTime 2019/10/3 8:41
*/
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException exception){
ResultUtil.responseJson(response,ResultUtil.resultCode(403,"未授权"));
}
}
4.5) UserAuthenticationEntryPointHandler.java未登录处理类
@Component
public class UserAuthenticationEntryPointHandler implements AuthenticationEntryPoint {
/**
* 用户未登录返回结果
* @Author Sans
* @CreateTime 2019/10/3 9:01
*/
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception){
ResultUtil.responseJson(response,ResultUtil.resultCode(401,"未登录"));
}
}
4.6) UserLoginFailureHandler.java录失败处理类
@Slf4j
@Component
public class UserLoginFailureHandler implements AuthenticationFailureHandler {
/**
* 登录失败返回结果
* @Author Sans
* @CreateTime 2019/10/3 9:12
*/
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception){
// 这些对于操作的处理类可以根据不同异常进行不同处理
if (exception instanceof UsernameNotFoundException){
log.info("【登录失败】"+exception.getMessage());
ResultUtil.responseJson(response,ResultUtil.resultCode(500,"用户名不存在"));
}
if (exception instanceof LockedException){
log.info("【登录失败】"+exception.getMessage());
ResultUtil.responseJson(response,ResultUtil.resultCode(500,"用户被冻结"));
}
if (exception instanceof BadCredentialsException){
log.info("【登录失败】"+exception.getMessage());
ResultUtil.responseJson(response,ResultUtil.resultCode(500,"用户名密码不正确"));
}
ResultUtil.responseJson(response,ResultUtil.resultCode(500,"登录失败"));
}
}
4.7) UserLoginSuccessHandler.java登录成功处理类
@Slf4j
@Component
public class UserLoginSuccessHandler implements AuthenticationSuccessHandler {
/**
* 登录成功返回结果
* @Author Sans
* @CreateTime 2019/10/3 9:27
*/
@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","200");
resultData.put("msg", "登录成功");
resultData.put("token",token);
ResultUtil.responseJson(response,resultData);
}
}
4.8) UserLogoutSuccessHandler.java登出成功处理类
@Component
public class UserLogoutSuccessHandler implements LogoutSuccessHandler {
/**
* 用户登出返回结果
* 这里应该让前端清除掉Token
* @Author Sans
* @CreateTime 2019/10/3 9:50
*/
@Override
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication){
Map<String,Object> resultData = new HashMap<>();
resultData.put("code","200");
resultData.put("msg", "登出成功");
SecurityContextHolder.clearContext();
ResultUtil.responseJson(response,ResultUtil.resultSuccess(resultData));
}
}
4.9) UserAuthenticationProvider.java自定义登录验证
@Component
public class UserAuthenticationProvider implements AuthenticationProvider {
@Autowired
private SelfUserDetailsService selfUserDetailsService;
@Autowired
private SysUserService sysUserService;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
// 获取表单输入中返回的用户名
String userName = (String) authentication.getPrincipal();
// 获取表单中输入的密码
String password = (String) authentication.getCredentials();
// 查询用户是否存在
SelfUserEntity userInfo = selfUserDetailsService.loadUserByUsername(userName);
if (userInfo == null) {
throw new UsernameNotFoundException("用户名不存在");
}
// 我们还要判断密码是否正确,这里我们的密码使用BCryptPasswordEncoder进行加密的
if (!new BCryptPasswordEncoder().matches(password, userInfo.getPassword())) {
throw new BadCredentialsException("密码不正确");
}
// 还可以加一些其他信息的判断,比如用户账号已停用等判断
if (userInfo.getStatus().equals("PROHIBIT")){
throw new LockedException("该用户已被冻结");
}
// 角色集合
Set<GrantedAuthority> authorities = new HashSet<>();
// 查询用户角色
List<SysRoleEntity> sysRoleEntityList = sysUserService.selectSysRoleByUserId(userInfo.getUserId());
for (SysRoleEntity sysRoleEntity: sysRoleEntityList){
authorities.add(new SimpleGrantedAuthority("ROLE_" + sysRoleEntity.getRoleName()));
}
userInfo.setAuthorities(authorities);
// 进行登录
return new UsernamePasswordAuthenticationToken(userInfo, password, authorities);
}
@Override
public boolean supports(Class<?> authentication) {
return true;
}
}
4.10) UserPermissionEvaluator.java自定义权限注解验证
@Component
public class UserPermissionEvaluator implements PermissionEvaluator {
@Autowired
private SysUserService sysUserService;
/**
* hasPermission鉴权方法
* 这里仅仅判断PreAuthorize注解中的权限表达式
* 实际中可以根据业务需求设计数据库通过targetUrl和permission做更复杂鉴权
* 当然targetUrl不一定是URL可以是数据Id还可以是管理员标识等,这里根据需求自行设计
* @Author Sans
* @CreateTime 2019/10/6 18:25
* @Param authentication 用户身份(在使用hasPermission表达式时Authentication参数默认会自动带上)
* @Param targetUrl 请求路径
* @Param permission 请求路径权限
* @Return boolean 是否通过
*/
@Override
public boolean hasPermission(Authentication authentication, Object targetUrl, Object permission) {
// 获取用户信息
SelfUserEntity selfUserEntity =(SelfUserEntity) authentication.getPrincipal();
// 查询用户权限(这里可以将权限放入缓存中提升效率)
Set<String> permissions = new HashSet<>();
List<SysMenuEntity> sysMenuEntityList = sysUserService.selectSysMenuByUserId(selfUserEntity.getUserId());
for (SysMenuEntity sysMenuEntity:sysMenuEntityList) {
permissions.add(sysMenuEntity.getPermission());
}
// 权限对比
if (permissions.contains(permission.toString())){
return true;
}
return false;
}
@Override
public boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission) {
return false;
}
}
至此securrity相关的支持类已经全部完成,你可以休息一下了,哈哈哈
至此securrity相关的支持类已经全部完成,你可以休息一下了,哈哈哈
至此securrity相关的支持类已经全部完成,你可以休息一下了,哈哈哈
5) 开始下一阶段的编码:本模块主要解释工具类
5.1)JWT工具类,用于创建token JWTTokenUtil.java
@Slf4j
public class JWTTokenUtil {
/**
* 私有化构造器
*/
private JWTTokenUtil(){}
/**
* 生成Token
* @Author Sans
* @CreateTime 2019/10/2 12:16
* @Param selfUserEntity 用户安全实体
* @Return Token
*/
public static String createAccessToken(SelfUserEntity selfUserEntity){
// 登陆成功生成JWT
String token = Jwts.builder()
// 放入用户名和用户ID
.setId(selfUserEntity.getUserId()+"")
// 主题
.setSubject(selfUserEntity.getUsername())
// 签发时间
.setIssuedAt(new Date())
// 签发者
.setIssuer("sans")
// 自定义属性 放入用户拥有权限
.claim("authorities", JSON.toJSONString(selfUserEntity.getAuthorities()))
// 失效时间
.setExpiration(new Date(System.currentTimeMillis() + JWTConfig.expiration))
// 签名算法和密钥
.signWith(SignatureAlgorithm.HS512, JWTConfig.secret)
.compact();
return token;
}
}
5.2) 结果工具类,预定义返回的结果集类型 ResultUtil.java
@Slf4j
public class ResultUtil {
/**
* 私有化构造器
*/
private ResultUtil(){}
/**
* 使用response输出JSON
* @Author Sans
* @CreateTime 2019/9/28 11:23
* @Param resultMap 数据
* @Return void
*/
public static void responseJson(ServletResponse response, Map<String, Object> resultMap){
PrintWriter out = null;
try {
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json");
out = response.getWriter();
out.println(JSON.toJSONString(resultMap));
} catch (Exception e) {
log.error("【JSON输出异常】"+e);
}finally{
if(out!=null){
out.flush();
out.close();
}
}
}
/**
* 返回成功示例
* @Author Sans
* @CreateTime 2019/9/28 11:29
* @Param resultMap 返回数据MAP
* @Return Map<String,Object> 返回数据MAP
*/
public static Map<String, Object> resultSuccess(Map<String, Object> resultMap){
resultMap.put("message","操作成功");
resultMap.put("code", 200);
return resultMap;
}
/**
* 返回失败示例
* @Author Sans
* @CreateTime 2019/9/28 11:31
* @Param resultMap 返回数据MAP
* @Return Map<String,Object> 返回数据MAP
*/
public static Map<String, Object> resultError(Map<String, Object> resultMap){
resultMap.put("message","操作失败");
resultMap.put("code",500);
return resultMap;
}
/**
* 通用示例
* @Author Sans
* @CreateTime 2019/9/28 11:35
* @Param code 信息码
* @Param msg 信息
* @Return Map<String,Object> 返回数据MAP
*/
public static Map<String, Object> resultCode(Integer code,String msg){
Map<String, Object> resultMap = new HashMap<>();
resultMap.put("message",msg);
resultMap.put("code",code);
return resultMap;
}
}
5.3) Security工具类,对于Security的信心获取
public class SecurityUtil {
/**
* 私有化构造器
*/
private SecurityUtil(){}
/**
* 获取当前用户信息
*/
public static SelfUserEntity getUserInfo(){
SelfUserEntity userDetails = (SelfUserEntity) SecurityContextHolder.getContext().getAuthentication() .getPrincipal();
return userDetails;
}
/**
* 获取当前用户ID
*/
public static Long getUserId(){
return getUserInfo().getUserId();
}
/**
* 获取当前用户账号
*/
public static String getUserName(){
return getUserInfo().getUsername();
}
}
至此我们本次项目的工具类就全部完成了,你可以出去吃个饭了,哈哈哈
至此我们本次项目的工具类就全部完成了,你可以出去吃个饭了,哈哈哈
至此我们本次项目的工具类就全部完成了,你可以出去吃个饭了,哈哈哈
6) 开始业务层的编码工作
6.1)mapper
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.sbs.security.dao.SysMenuDao">
</mapper>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.sbs.security.dao.SysRoleDao">
</mapper>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.sbs.security.dao.SysRoleMenuDao">
</mapper>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.sbs.security.dao.SysUserDao">
<!-- 查询用户的所有权限 -->
<select id="selectSysMenuByUserId" resultType="com.sbs.security.model.SysMenuEntity" parameterType="long">
SELECT DISTINCT m.* FROM sys_user_role ur
LEFT JOIN sys_role_menu rm ON ur.role_id = rm.role_id
LEFT JOIN sys_menu m ON rm.menu_id = m.menu_id
WHERE ur.user_id = #{userId}
</select>
<!-- 查询用户所有角色 -->
<select id="selectSysRoleByUserId" resultType="com.sbs.security.model.SysRoleEntity" parameterType="long">
SELECT sr.* FROM sys_role sr
LEFT JOIN sys_user_role se ON se.role_id = sr.role_id
WHERE se.user_id = #{userId}
</select>
</mapper>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.sbs.security.dao.SysUserRoleDao">
</mapper>
6.2) model
@Data
@TableName("sys_menu")
public class SysMenuEntity implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 权限ID
*/
@TableId
private Long menuId;
/**
* 权限名称
*/
private String name;
/**
* 权限标识
*/
private String permission;
}
@Data
@TableName("sys_role")
public class SysRoleEntity implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 角色ID
*/
@TableId
private Long roleId;
/**
* 角色名称
*/
private String roleName;
}
@Data
@TableName("sys_role_menu")
public class SysRoleMenuEntity implements Serializable {
private static final long serialVersionUID = 1L;
/**
* ID
*/
@TableId
private Long id;
/**
* 角色ID
*/
private Long roleId;
/**
* 权限ID
*/
private Long menuId;
}
@Data
@TableName("sys_user")
public class SysUserEntity implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 用户ID
*/
@TableId
private Long userId;
/**
* 用户名
*/
private String username;
/**
* 密码
*/
private String password;
/**
* 状态:NORMAL正常 PROHIBIT禁用
*/
private String status;
}
@Data
@TableName("sys_user_role")
public class SysUserRoleEntity implements Serializable {
private static final long serialVersionUID = 1L;
/**
* ID
*/
@TableId
private Long id;
/**
* 用户ID
*/
private Long userId;
/**
* 角色ID
*/
private Long roleId;
}
6.3) dao
@Mapper
public interface SysMenuDao extends BaseMapper<SysMenuEntity> {
}
@Mapper
public interface SysRoleDao extends BaseMapper<SysRoleEntity> {
}
@Mapper
public interface SysRoleMenuDao extends BaseMapper<SysRoleMenuEntity> {
}
@Mapper
public interface SysUserDao extends BaseMapper<SysUserEntity> {
/**
* 通过用户ID查询角色集合
* @Author Sans
* @CreateTime 2019/9/18 18:01
* @Param userId 用户ID
* @Return List<SysRoleEntity> 角色名集合
*/
List<SysRoleEntity> selectSysRoleByUserId(Long userId);
/**
* 通过用户ID查询权限集合
* @Author Sans
* @CreateTime 2019/9/18 18:01
* @Param userId 用户ID
* @Return List<SysMenuEntity> 角色名集合
*/
List<SysMenuEntity> selectSysMenuByUserId(Long userId);
}
@Mapper
public interface SysUserRoleDao extends BaseMapper<SysUserRoleEntity> {
}
6.4) service
public interface SysMenuService extends IService<SysMenuEntity> {
}
public interface SysRoleMenuService extends IService<SysRoleMenuEntity> {
}
public interface SysRoleService extends IService<SysRoleEntity> {
}
public interface SysUserRoleService extends IService<SysUserRoleEntity> {
}
public interface SysUserService extends IService<SysUserEntity> {
/**
* 根据用户名查询实体
* @Author Sans
* @CreateTime 2019/9/14 16:30
* @Param username 用户名
* @Return SysUserEntity 用户实体
*/
SysUserEntity selectUserByName(String username);
/**
* 根据用户ID查询角色集合
* @Author Sans
* @CreateTime 2019/9/18 18:01
* @Param userId 用户ID
* @Return List<SysRoleEntity> 角色名集合
*/
List<SysRoleEntity> selectSysRoleByUserId(Long userId);
/**
* 根据用户ID查询权限集合
* @Author Sans
* @CreateTime 2019/9/18 18:01
* @Param userId 用户ID
* @Return List<SysMenuEntity> 角色名集合
*/
List<SysMenuEntity> selectSysMenuByUserId(Long userId);
}
6.5) impl
@Service("sysMenuService")
public class SysMenuServiceImpl extends ServiceImpl<SysMenuDao, SysMenuEntity> implements SysMenuService {
}
@Service("sysRoleMenuService")
public class SysRoleMenuServiceImpl extends ServiceImpl<SysRoleMenuDao, SysRoleMenuEntity> implements SysRoleMenuService {
}
@Service("sysRoleService")
public class SysRoleServiceImpl extends ServiceImpl<SysRoleDao, SysRoleEntity> implements SysRoleService {
}
@Service("sysUserRoleService")
public class SysUserRoleServiceImpl extends ServiceImpl<SysUserRoleDao, SysUserRoleEntity> implements SysUserRoleService {
}
@Service("sysUserService")
public class SysUserServiceImpl extends ServiceImpl<SysUserDao, SysUserEntity> implements SysUserService {
/**
* 根据用户名查询实体
* @Author Sans
* @CreateTime 2019/9/14 16:30
* @Param username 用户名
* @Return SysUserEntity 用户实体
*/
@Override
public SysUserEntity selectUserByName(String username) {
QueryWrapper<SysUserEntity> queryWrapper = new QueryWrapper<>();
queryWrapper.lambda().eq(SysUserEntity::getUsername,username);
return this.baseMapper.selectOne(queryWrapper);
}
/**
* 通过用户ID查询角色集合
* @Author Sans
* @CreateTime 2019/9/18 18:01
* @Param userId 用户ID
* @Return List<SysRoleEntity> 角色名集合
*/
@Override
public List<SysRoleEntity> selectSysRoleByUserId(Long userId) {
return this.baseMapper.selectSysRoleByUserId(userId);
}
/**
* 根据用户ID查询权限集合
* @Author Sans
* @CreateTime 2019/9/18 18:01
* @Param userId 用户ID
* @Return List<SysMenuEntity> 角色名集合
*/
@Override
public List<SysMenuEntity> selectSysMenuByUserId(Long userId) {
return this.baseMapper.selectSysMenuByUserId(userId);
}
}
6.6) controller
@RestController
@RequestMapping("/admin")
public class AdminController {
@Autowired
private SysUserService sysUserService;
@Autowired
private SysRoleService sysRoleService;
@Autowired
private SysMenuService sysMenuService;
/**
* 管理端信息
* @Author Sans
* @CreateTime 2019/10/2 14:22
* @Return Map<String,Object> 返回数据MAP
*/
@PreAuthorize("hasRole('ADMIN')")
@RequestMapping(value = "/info",method = RequestMethod.GET)
public Map<String,Object> userLogin(){
Map<String,Object> result = new HashMap<>();
SelfUserEntity userDetails = SecurityUtil.getUserInfo();
result.put("title","管理端信息");
result.put("data",userDetails);
return ResultUtil.resultSuccess(result);
}
/**
* 拥有ADMIN或者USER角色可以访问
* @Author Sans
* @CreateTime 2019/10/2 14:22
* @Return Map<String,Object> 返回数据MAP
*/
@PreAuthorize("hasAnyRole('ADMIN','USER')")
@RequestMapping(value = "/list",method = RequestMethod.GET)
public Map<String,Object> list(){
Map<String,Object> result = new HashMap<>();
List<SysUserEntity> sysUserEntityList = sysUserService.list();
result.put("title","拥有用户或者管理员角色都可以查看");
result.put("data",sysUserEntityList);
return ResultUtil.resultSuccess(result);
}
/**
* 拥有ADMIN和USER角色可以访问
* @Author Sans
* @CreateTime 2019/10/2 14:22
* @Return Map<String,Object> 返回数据MAP
*/
@PreAuthorize("hasRole('ADMIN') and hasRole('USER')")
@RequestMapping(value = "/menuList",method = RequestMethod.GET)
public Map<String,Object> menuList(){
Map<String,Object> result = new HashMap<>();
List<SysMenuEntity> sysMenuEntityList = sysMenuService.list();
result.put("title","拥有用户和管理员角色都可以查看");
result.put("data",sysMenuEntityList);
return ResultUtil.resultSuccess(result);
}
/**
* 拥有sys:user:info权限可以访问
* hasPermission 第一个参数是请求路径 第二个参数是权限表达式
* @Author Sans
* @CreateTime 2019/10/2 14:22
* @Return Map<String,Object> 返回数据MAP
*/
@PreAuthorize("hasPermission('/admin/userList','sys:user:info')")
@RequestMapping(value = "/userList",method = RequestMethod.GET)
public Map<String,Object> userList(){
Map<String,Object> result = new HashMap<>();
List<SysUserEntity> sysUserEntityList = sysUserService.list();
result.put("title","拥有sys:user:info权限都可以查看");
result.put("data",sysUserEntityList);
return ResultUtil.resultSuccess(result);
}
/**
* 拥有ADMIN角色和sys:role:info权限可以访问
* @Author Sans
* @CreateTime 2019/10/2 14:22
* @Return Map<String,Object> 返回数据MAP
*/
@PreAuthorize("hasRole('ADMIN') and hasPermission('/admin/adminRoleList','sys:role:info')")
@RequestMapping(value = "/adminRoleList",method = RequestMethod.GET)
public Map<String,Object> adminRoleList(){
Map<String,Object> result = new HashMap<>();
List<SysRoleEntity> sysRoleEntityList = sysRoleService.list();
result.put("title","拥有ADMIN角色和sys:role:info权限可以访问");
result.put("data",sysRoleEntityList);
return ResultUtil.resultSuccess(result);
}
}
@RestController
@RequestMapping("/index")
public class IndexController {
/**
* 首页
* @Author Sans
* @CreateTime 2019/10/2 15:23
* @Return Map<String,Object> 返回数据MAP
*/
@RequestMapping(value = "/info",method = RequestMethod.GET)
public Map<String,Object> userLogin(){
// 组装参数
Map<String,Object> result = new HashMap<>();
result.put("title","这里是首页不需要权限和登录拦截");
return ResultUtil.resultSuccess(result);
}
}
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private SysMenuService sysMenuService;
/**
* 用户端信息
* @Author Sans
* @CreateTime 2019/10/2 14:52
* @Return Map<String,Object> 返回数据MAP
*/
@RequestMapping(value = "/info",method = RequestMethod.GET)
public Map<String,Object> userLogin(){
Map<String,Object> result = new HashMap<>();
SelfUserEntity userDetails = (SelfUserEntity) SecurityContextHolder.getContext().getAuthentication() .getPrincipal();
result.put("title","用户端信息");
result.put("data",userDetails);
return ResultUtil.resultSuccess(result);
}
/**
* 拥有USER角色和sys:user:info权限可以访问
* @Author Sans
* @CreateTime 2019/10/2 14:22
* @Return Map<String,Object> 返回数据MAP
*/
@PreAuthorize("hasRole('USER') and hasPermission('/user/menuList','sys:user:info')")
@RequestMapping(value = "/menuList",method = RequestMethod.GET)
public Map<String,Object> sysMenuEntity(){
Map<String,Object> result = new HashMap<>();
List<SysMenuEntity> sysMenuEntityList = sysMenuService.list();
result.put("title","拥有USER角色和sys:user:info权限可以访问");
result.put("data",sysMenuEntityList);
return ResultUtil.resultSuccess(result);
}
}
至此业务完全完成了,开始我们的骚操作吧
至此业务完全完成了,开始我们的骚操作吧
至此业务完全完成了,开始我们的骚操作吧
7)各种配置类。关键代码来了
@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 * 1000;
}
public void setAntMatchers(String antMatchers) {
this.antMatchers = antMatchers;
}
}
@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 Sans
* @CreateTime 2019/10/1 14:00
*/
@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 Sans
* @CreateTime 2019/10/1 16:56
* @Param http 请求
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
// 不进行权限验证的请求或资源(从配置文件中读取)
.antMatchers(JWTConfig.antMatchers.split(",")).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()));
}
}
配置编码结束,是不是发现没有数模呢,哈哈哈
8) 数据模型
DROP TABLE IF EXISTS `sys_menu`;
CREATE TABLE `sys_menu` (
`menu_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID',
`name` varchar(50) NOT NULL COMMENT '权限名称',
`permission` varchar(200) DEFAULT NULL COMMENT '权限标识',
PRIMARY KEY (`menu_id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=87 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='权限表';
-- ----------------------------
-- Records of sys_menu
-- ----------------------------
INSERT INTO `sys_menu` VALUES ('1', '查看用户信息', 'sys:user:info');
INSERT INTO `sys_menu` VALUES ('2', '查看所有权限', 'sys:menu:info');
INSERT INTO `sys_menu` VALUES ('3', '查看所有角色', 'sys:role:info');
-- ----------------------------
-- Table structure for sys_role
-- ----------------------------
DROP TABLE IF EXISTS `sys_role`;
CREATE TABLE `sys_role` (
`role_id` bigint(11) NOT NULL AUTO_INCREMENT COMMENT '角色ID',
`role_name` varchar(50) NOT NULL COMMENT '角色名称',
PRIMARY KEY (`role_id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='角色表';
-- ----------------------------
-- Records of sys_role
-- ----------------------------
INSERT INTO `sys_role` VALUES ('1', 'ADMIN');
INSERT INTO `sys_role` VALUES ('2', 'USER');
-- ----------------------------
-- Table structure for sys_role_menu
-- ----------------------------
DROP TABLE IF EXISTS `sys_role_menu`;
CREATE TABLE `sys_role_menu` (
`id` bigint(11) NOT NULL AUTO_INCREMENT COMMENT 'ID',
`role_id` bigint(11) DEFAULT NULL COMMENT '角色ID',
`menu_id` bigint(11) DEFAULT NULL COMMENT '权限ID',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='角色与权限关系表';
-- ----------------------------
-- Records of sys_role_menu
-- ----------------------------
INSERT INTO `sys_role_menu` VALUES ('1', '1', '1');
INSERT INTO `sys_role_menu` VALUES ('2', '1', '2');
INSERT INTO `sys_role_menu` VALUES ('3', '1', '3');
INSERT INTO `sys_role_menu` VALUES ('4', '2', '1');
-- ----------------------------
-- Table structure for sys_user
-- ----------------------------
DROP TABLE IF EXISTS `sys_user`;
CREATE TABLE `sys_user` (
`user_id` bigint(11) NOT NULL AUTO_INCREMENT COMMENT '用户ID',
`username` varchar(50) NOT NULL COMMENT '用户名',
`password` varchar(100) DEFAULT NULL COMMENT '密码',
`status` varchar(10) DEFAULT NULL COMMENT '状态 PROHIBIT:禁用 NORMAL:正常',
PRIMARY KEY (`user_id`) USING BTREE,
UNIQUE KEY `username` (`username`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='系统用户表';
-- ----------------------------
-- Records of sys_user
-- ----------------------------
INSERT INTO `sys_user` VALUES ('1', 'admin', '$2a$10$5T851lZ7bc2U87zjt/9S6OkwmLW62tLeGLB2aCmq3XRZHA7OI7Dqa', 'NORMAL');
INSERT INTO `sys_user` VALUES ('2', 'user', '$2a$10$szHoqQ64g66PymVJkip98.Fap21Csy8w.RD8v5Dhq08BMEZ9KaSmS', 'NORMAL');
-- ----------------------------
-- Table structure for sys_user_role
-- ----------------------------
DROP TABLE IF EXISTS `sys_user_role`;
CREATE TABLE `sys_user_role` (
`id` bigint(11) NOT NULL AUTO_INCREMENT COMMENT 'ID',
`user_id` bigint(11) DEFAULT NULL COMMENT '用户ID',
`role_id` bigint(11) DEFAULT NULL COMMENT '角色ID',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='用户与角色关系表';
-- ----------------------------
-- Records of sys_user_role
-- ----------------------------
INSERT INTO `sys_user_role` VALUES ('1', '1', '1');
INSERT INTO `sys_user_role` VALUES ('2', '2', '2');