1、导入pom依赖
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.2.0</version> <relativePath/> </parent> <groupId>com.atguigu</groupId> <artifactId>newversion-springboot</artifactId> <version>0.0.1-SNAPSHOT</version> <properties> <java.version>17</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.30</version> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.4.1</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>3.0.3</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <!--swagger测试--> <dependency> <groupId>com.github.xiaoymin</groupId> <artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId> <version>4.1.0</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.1</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>2.0.21</version> </dependency> <dependency> <groupId>javax.xml.bind</groupId> <artifactId>jaxb-api</artifactId> <version>2.3.1</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <excludes> <exclude> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </exclude> </excludes> </configuration> </plugin> </plugins> </build> </project>
2、编写application.yml 配置文件
server: port: 6666 spring: application: name: boot3-security datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/security-demo username: root password: 123456 data: redis: host: 106.54.26.206 port: 6379 mybatis-plus: mapper-locations: classpath:mapper/*.xml configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
3、编写启动类
@SpringBootApplication public class NewVersionApplication { public static void main(String[] args) { SpringApplication.run(NewVersionApplication.class, args); } }
4、准备工具类、通用类
package com.atguigu.newversion.utils; import io.jsonwebtoken.*; import org.springframework.util.StringUtils; import java.util.Date; public class JwtHelper { // token 令牌的过期时间 private static long tokenExpiration = 365 * 24 * 60 * 60 * 1000; // 令牌密钥 private static String tokenSignKey = "123456"; public static String createToken(Long userId, String username) { String token = Jwts.builder() .setSubject("AUTH-USER") .setExpiration(new Date(System.currentTimeMillis() + tokenExpiration)) .claim("userId", userId) .claim("username", username) .signWith(SignatureAlgorithm.HS512, tokenSignKey) .compressWith(CompressionCodecs.GZIP) .compact(); return token; } public static Long getUserId(String token) { try { if (StringUtils.isEmpty(token)) return null; Jws<Claims> claimsJws = Jwts.parser().setSigningKey(tokenSignKey).parseClaimsJws(token); Claims claims = claimsJws.getBody(); Integer userId = (Integer) claims.get("userId"); return userId.longValue(); } catch (Exception e) { e.printStackTrace(); return null; } } public static String getUsername(String token) { try { if (StringUtils.isEmpty(token)) return ""; Jws<Claims> claimsJws = Jwts.parser().setSigningKey(tokenSignKey).parseClaimsJws(token); Claims claims = claimsJws.getBody(); return (String) claims.get("username"); } catch (Exception e) { e.printStackTrace(); return null; } } public static void main(String[] args) { String token = JwtHelper.createToken(1L, "admin"); System.out.println(token); System.out.println(JwtHelper.getUserId(token)); System.out.println(JwtHelper.getUsername(token)); } }
package com.atguigu.newversion.utils; import com.fasterxml.jackson.databind.ObjectMapper; import jakarta.servlet.http.HttpServletResponse; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import java.io.IOException; import java.util.Map; public class ResponseUtil { public static void out(HttpServletResponse response, Object result) { ObjectMapper mapper = new ObjectMapper(); response.setStatus(HttpStatus.OK.value()); response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE); try { mapper.writeValue(response.getWriter(), result); } catch (IOException e) { e.printStackTrace(); } } }
package com.atguigu.newversion.utils; import lombok.Data; public enum ResultCodeEnum { SUCCESS(200, "请求成功"), FAILED(500, "操作失败"), TOKEN_FAILED(501, "操作超时"), NO_PERMISSION(403, "无权限"), LOGOUT(502, "已退出"), SAFE_CHECK_FAILED(503, "此操作需要先进行安全验证"), NONE(100, "无"); private int code; private String msg; private ResultCodeEnum(int code, String msg) { this.code = code; this.msg = msg; } public int getCode() { return code; } public void setCode(int code) { this.code = code; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } }
package com.atguigu.newversion.utils; import lombok.Data; import java.io.Serializable; @Data public class ResultData<T> implements Serializable { private int code; private String msg; private T data; public static <T> ResultData<T> success(){ ResultData<T> resultData=new ResultData<T>(); resultData.setMsg("操作成功"); resultData.setCode(ResultCodeEnum.SUCCESS.getCode()); return resultData; } public static <T> ResultData<T> success(T data){ ResultData<T> resultData=new ResultData<T>(); resultData.setData(data); resultData.setMsg("操作成功"); resultData.setCode(ResultCodeEnum.SUCCESS.getCode()); return resultData; } public static <T> ResultData<T> success(String msg, T data){ ResultData<T> resultData=new ResultData<T>(); resultData.setData(data); resultData.setMsg(msg); resultData.setCode(ResultCodeEnum.SUCCESS.getCode()); return resultData; } public static <T> ResultData<T> error() { ResultData<T> resultData = new ResultData<>(); resultData.setCode(ResultCodeEnum.FAILED.getCode()); resultData.setMsg("系统异常"); return resultData; } public static <T> ResultData<T> error(String msg) { ResultData<T> resultData = new ResultData<>(); resultData.setCode(ResultCodeEnum.FAILED.getCode()); resultData.setMsg(msg); return resultData; } }
5、添加 实体类
package com.atguigu.newversion.entity.base; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableLogic; import lombok.Data; import java.io.Serializable; import java.util.Date; import java.util.HashMap; import java.util.Map; @Data public class BaseEntity implements Serializable { @TableId(type = IdType.AUTO) private Long id; @TableField("create_time") private Date createTime; @TableField("update_time") private Date updateTime; @TableLogic @TableField("is_deleted") private Integer isDeleted; @TableField(exist = false) private Map<String,Object> param = new HashMap<>(); }
package com.atguigu.newversion.entity; import com.atguigu.newversion.entity.base.BaseEntity; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; import java.util.List; @Data @TableName("sys_user") public class SysUser extends BaseEntity { private static final long serialVersionUID = 1L; @TableField("username") private String username; @TableField("password") private String password; @TableField("name") private String name; @TableField("phone") private String phone; @TableField("head_url") private String headUrl; @TableField("dept_id") private Long deptId; @TableField("post_id") private Long postId; @TableField("description") private String description; @TableField("open_id") private String openId; @TableField("status") private Integer status; @TableField(exist = false) private List<SysRole> roleList; //岗位 @TableField(exist = false) private String postName; //部门 @TableField(exist = false) private String deptName; }
@Data @TableName("sys_role") public class SysRole extends BaseEntity { private static final long serialVersionUID = 1L; @TableField("role_name") private String roleName; @TableField("role_code") private String roleCode; @TableField("description") private String description; }
@Data @TableName("sys_menu") public class SysMenu extends BaseEntity { private static final long serialVersionUID = 1L; @TableField("parent_id") private Long parentId; @TableField("name") private String name; @TableField("type") private Integer type; @TableField("path") private String path; @TableField("component") private String component; @TableField("perms") private String perms; @TableField("icon") private String icon; @TableField("sort_value") private Integer sortValue; @TableField("status") private Integer status; // 下级列表 @TableField(exist = false) private List<SysMenu> children; //是否选中 @TableField(exist = false) private boolean isSelect; }
@Data @TableName("sys_role_menu") public class SysRoleMenu extends BaseEntity { private static final long serialVersionUID = 1L; @TableField("role_id") private Long roleId; @TableField("menu_id") private Long menuId; }
@Data @TableName("sys_user_role") public class SysUserRole extends BaseEntity { private static final long serialVersionUID = 1L; @TableField("role_id") private Long roleId; @TableField("user_id") private Long userId; }
6、编写一个继承 SpringSecurity里的User类 用于封装数据 MySecurityUser类
import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.userdetails.User; import java.util.Collection; public class MySecurityUser extends User { /** * 我们自己的用户实体对象,要调取用户信息时直接获取这个实体对象。(这里我就不写get/set方法了) */ private SysUser sysUser; public MySecurityUser(SysUser sysUser, Collection<? extends GrantedAuthority> authorities) { super(sysUser.getUsername(), sysUser.getPassword(), authorities); this.sysUser = sysUser; } public SysUser getSysUser() { return sysUser; } public void setSysUser(SysUser sysUser) { this.sysUser = sysUser; } }
7、编写一个继承Security 中的 UserDetailService接口 MyUserDetailService 用于查询用户信息和权限权限
@Service public class MyUserDetailsService implements UserDetailsService { @Resource SysUserMapper sysUserMapper; @Resource private SysMenuService sysMenuService; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { LambdaQueryWrapper<SysUser> wrapper = new LambdaQueryWrapper<>(); wrapper.eq(SysUser::getUsername,username); SysUser sysUser = sysUserMapper.selectOne(wrapper); List<String> userPermsList = sysMenuService.findUserPermsList(sysUser.getId()); List<SimpleGrantedAuthority> authorities = new ArrayList<>(); for (String perm : userPermsList) { authorities.add(new SimpleGrantedAuthority(perm.trim())); } return new MySecurityUser(sysUser, authorities); } }
8、编写一个 处理登入请求的过滤器
public class TokenLoginFilter extends UsernamePasswordAuthenticationFilter { private RedisTemplate<String,Object> redisTemplate; public TokenLoginFilter(AuthenticationManager authenticationManager, RedisTemplate redisTemplate) { this.setAuthenticationManager(authenticationManager); this.setPostOnly(false); //指定登录接口及提交方式,可以指定任意路径 this.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/user/login","POST")); this.redisTemplate = redisTemplate; } @Override public Authentication attemptAuthentication(HttpServletRequest req, HttpServletResponse res) throws AuthenticationException { try { Map loginVo = new ObjectMapper().readValue(req.getInputStream(), Map.class); //这个方法让Security 去调用 UserDetailsService loadUserByUsername方法 Authentication authenticationToken = new UsernamePasswordAuthenticationToken(loginVo.get("username"), loginVo.get("password")); return this.getAuthenticationManager().authenticate(authenticationToken); } catch (IOException e) { throw new RuntimeException(e); } } /** * 登录成功 * @param request * @param response * @param chain * @param auth * @throws IOException * @throws ServletException */ @Override protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication auth) throws IOException, ServletException { System.out.println("《==========================登入成功=========================》"); MySecurityUser customUser = (MySecurityUser) auth.getPrincipal(); String token = JwtHelper.createToken(customUser.getSysUser().getId(), customUser.getSysUser().getUsername()); //保存权限数据 redisTemplate.opsForValue().set(customUser.getUsername(), JSON.toJSONString(customUser.getAuthorities())); //通过response返回数据 ResponseUtil.out(response, ResultData.success(token)); } /** * 登录失败 * @param request * @param response * @param e * @throws IOException * @throws ServletException */ @Override protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException { System.out.println("《==========================登入失败=========================》"); if(e.getCause() instanceof RuntimeException) { ResponseUtil.out(response, ResultData.error(e.getMessage())); } else { ResponseUtil.out(response, ResultData.error("登入失败用户名或密码错误")); } } }
9、编写一个过滤所有方法的过滤器获取token 校验用户是否登入
public class TokenAuthenticationFilter extends OncePerRequestFilter { private RedisTemplate redisTemplate; public TokenAuthenticationFilter(RedisTemplate redisTemplate) { this.redisTemplate = redisTemplate; } @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { logger.info("uri:"+request.getRequestURI()); //如果是登录接口,直接放行 if("/user/login".equals(request.getRequestURI())) { chain.doFilter(request, response); return; } UsernamePasswordAuthenticationToken authentication = getAuthentication(request,response); if(null != authentication) { //最终把用户的所以信息放到安全 上下文对象里面 在项目任何地方使用 SecurityContextHolder.getContext().setAuthentication(authentication); chain.doFilter(request, response); } else { Map<String, Object> map = new HashMap<>(); map.put("msg", "没有登入请先登入"); //通过response返回数据 ResponseUtil.out(response, map); } } private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request,HttpServletResponse response) { String token = request.getHeader("token"); if (!StringUtils.isEmpty(token)) { String username = JwtHelper.getUsername(token); Long userId = JwtHelper.getUserId(token); if (!StringUtils.isEmpty(username)) { String authoritiesString = (String) redisTemplate.opsForValue().get(username); if(authoritiesString == null){ ResponseUtil.out(response, ResultData.error("令牌已经失效请重新登入")); } List<Map> mapList = JSON.parseArray(authoritiesString, Map.class); List<SimpleGrantedAuthority> authorities = new ArrayList<>(); for (Map map : mapList) { authorities.add(new SimpleGrantedAuthority((String)map.get("authority"))); } return new UsernamePasswordAuthenticationToken(username, userId, authorities); } else { return new UsernamePasswordAuthenticationToken(username, null, new ArrayList<>()); } } return null; } }
10、编写redis的配置类
@Configuration public class RedisConfig { @Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) { RedisTemplate<String, Object> template = new RedisTemplate<>(); template.setConnectionFactory(redisConnectionFactory); // 设置key的序列化方式为StringRedisSerializer template.setKeySerializer(new StringRedisSerializer()); template.setHashKeySerializer(new StringRedisSerializer()); // 设置value的序列化方式,根据需要选择合适的序列化器 template.setValueSerializer(new GenericJackson2JsonRedisSerializer()); template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer()); return template; } @Bean public CacheManager cacheManager(LettuceConnectionFactory connectionFactory) { //定义序列化器 GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer(); StringRedisSerializer stringRedisSerializer = new StringRedisSerializer(); RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig() //过期时间600秒 .entryTtl(Duration.ofSeconds(600)) // 配置序列化 .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(stringRedisSerializer)) .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(genericJackson2JsonRedisSerializer)); RedisCacheManager cacheManager = RedisCacheManager.builder(connectionFactory) .cacheDefaults(config) .build(); return cacheManager; } }
11、SpringSevurity的核心配置类
@EnableMethodSecurity @EnableWebSecurity//@EnableWebSecurity是开启SpringSecurity的默认行为 @Configuration public class MyWebSecurityConfig { @Autowired private MyUserDetailsService myUserDetailsService; @Autowired private RedisTemplate<String,Object> redisTemplate; @Bean public AuthenticationManager authenticationManager(){ DaoAuthenticationProvider provider = new DaoAuthenticationProvider(); provider.setUserDetailsService(myUserDetailsService); provider.setPasswordEncoder(passwordEncoder()); ProviderManager providerManager = new ProviderManager(provider); return providerManager; } //密码加密器 @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Bean SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { return http.authorizeHttpRequests(conf ->conf.requestMatchers(paths).permitAll() .anyRequest().authenticated()) .csrf(AbstractHttpConfigurer::disable) .sessionManagement(conf ->conf.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .addFilterBefore(new TokenAuthenticationFilter(redisTemplate), UsernamePasswordAuthenticationFilter.class) .addFilter(new TokenLoginFilter(authenticationManager(),redisTemplate)) .build(); } private final String[] paths = { "/favicon.ico","/swagger-resources/**", "/webjars/**", "/v2/**", "/swagger-ui.html/**", "/doc.html","/v3/**" }; @Bean WebSecurityCustomizer ignoredUrlsCustomizer() { return (web) -> web.ignoring().requestMatchers(paths); } }
12、根据userID 查询用户拥有的权限数据
<?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.atguigu.newversion.mapper.SysMenuMapper"> <resultMap id="sysMenuMap" type="com.atguigu.newversion.entity.SysMenu" autoMapping="true"> </resultMap> <select id="findListByUserId" resultMap="sysMenuMap"> select distinct m.id,m.parent_id,m.name,m.type,m.path,m.component,m.perms,m.icon,m.sort_value,m.status,m.create_time,m.update_time,m.is_deleted from sys_menu m inner join sys_role_menu rm on rm.menu_id = m.id inner join sys_user_role ur on ur.role_id = rm.role_id where ur.user_id=#{userId} and m.status = 1 and rm.is_deleted = 0 and ur.is_deleted = 0 and m.is_deleted = 0 </select> </mapper>
标签:TableField,Mybatisplus,String,private,登入,import,new,最新版,public From: https://www.cnblogs.com/shunWcs/p/18138480