002云E办项目之登录模块
如果你没有看第一篇文章,建议你去看一下
文章链接
- 好好看每一步
- 你可以知道什么是逆向工程
- 你可以在这里好好学习一下登录模块
- 由于本的sql语句放在001云E办项目创建
一、 逆向工程(创建AutoGenerator项目)
- AutoGenerator是什么
AutoGenerator 是 MyBatis-Plus 的代码生成器,通过 AutoGenerator 可以快速生成 Pojo、Mapper、
Mapper XML、Service、Controller 等各个模块的代码。 - AutoGenerator能干什么
对于单表而言,几乎是一个全能的工具,极大的提升了开发效率。更多的关注业务逻辑的实现。 - 怎么使用呢?
创建建AutoGenerator项目:这个项目时从maven里面创建原始类型,勾选那个archer-quickstart
AutoGenerator本身和我们项目没有关联,所以可以单独新建为一个Project,这边也做成Maven聚
合项目里的一个子项目 - 导入依赖
<?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.0http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><artifactId>voa</artifactId><groupId>com.mldn</groupId><version>0.0.1-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>voa-generator</artifactId><name>voa-generator</name><!-- FIXME change it to the project's website --><url>http://www.example.com</url><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><maven.compiler.source>1.7</maven.compiler.source><maven.compiler.target>1.7</maven.compiler.target></properties><dependencies><!--web 依赖--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!--mybatis-plus 依赖--><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.3.1.tmp</version></dependency><!--mybatis-plus 代码生成器依赖--><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-generator</artifactId><version>3.3.1.tmp</version></dependency><!--freemarker 依赖--><dependency><groupId>org.freemarker</groupId><artifactId>freemarker</artifactId></dependency><!--mysql 依赖--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.11</version><scope>test</scope></dependency></dependencies><build><pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) --><plugins><!-- clean lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#clean_Lifecycle --><plugin><artifactId>maven-clean-plugin</artifactId><version>3.1.0</version></plugin><!-- default lifecycle, jar packaging: see https://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging --><plugin><artifactId>maven-resources-plugin</artifactId><version>3.0.2</version></plugin><plugin><artifactId>maven-compiler-plugin</artifactId><version>3.8.0</version></plugin><plugin><artifactId>maven-surefire-plugin</artifactId><version>2.22.1</version></plugin><plugin><artifactId>maven-jar-plugin</artifactId><version>3.0.2</version></plugin><plugin><artifactId>maven-install-plugin</artifactId><version>2.5.2</version></plugin><plugin><artifactId>maven-deploy-plugin</artifactId><version>2.8.2</version></plugin><!-- site lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle --><plugin><artifactId>maven-site-plugin</artifactId><version>3.7.1</version></plugin><plugin><artifactId>maven-project-info-reports-plugin</artifactId><version>3.0.0</version></plugin></plugins></pluginManagement></build>
</project>
- 编写CodeGenerator工具类来使用
package com.mldn.generator;import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.FileOutConfig;
import com.baomidou.mybatisplus.generator.config.GlobalConfig;
import com.baomidou.mybatisplus.generator.config.PackageConfig;
import com.baomidou.mybatisplus.generator.config.StrategyConfig;
import com.baomidou.mybatisplus.generator.config.TemplateConfig;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
/*** 执行 main 方法控制台输入模块表名回车自动生成对应项目目录中** @author zhoubin* @since 1.0.0*/
public class CodeGenerator {public static String scanner(String tip) {Scanner scanner = new Scanner(System.in);StringBuilder help = new StringBuilder();help.append("请输入" + tip + ":");System.out.println(help.toString());if (scanner.hasNext()) {String ipt = scanner.next();if (StringUtils.isNotEmpty(ipt)) {return ipt;}}throw new MybatisPlusException("请输入正确的" + tip + "!");}public static void main(String[] args) {// 代码生成器AutoGenerator mpg = new AutoGenerator();// 全局配置GlobalConfig gc = new GlobalConfig();final String projectPath = System.getProperty("user.dir");gc.setOutputDir(projectPath + "/yeb-generator/src/main/java");//作者gc.setAuthor("zhoubin");//打开输出目录gc.setOpen(false);//xml开启 BaseResultMapgc.setBaseResultMap(true);//xml 开启BaseColumnListgc.setBaseColumnList(true);// 实体属性 Swagger2 注解gc.setSwagger2(true);mpg.setGlobalConfig(gc);// 数据源配置DataSourceConfig dsc = new DataSourceConfig();dsc.setUrl("jdbc:mysql://localhost:3306/yeb? useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia" +"/Shanghai");dsc.setDriverName("com.mysql.cj.jdbc.Driver");dsc.setUsername("root");dsc.setPassword("111");mpg.setDataSource(dsc);// 包配置PackageConfig pc = new PackageConfig();pc.setParent("com.xxxx").setEntity("pojo").setMapper("mapper").setService("service").setServiceImpl("service.impl").setController("controller");mpg.setPackageInfo(pc);// 自定义配置InjectionConfig cfg = new InjectionConfig() {@Overridepublic void initMap() {// to do nothing}};// 如果模板引擎是 freemarkerString templatePath = "/templates/mapper.xml.ftl";// 如果模板引擎是 velocity// String templatePath = "/templates/mapper.xml.vm";// 自定义输出配置List<FileOutConfig> focList = new ArrayList<>();// 自定义配置会被优先输出focList.add(new FileOutConfig(templatePath) {@Overridepublic String outputFile(TableInfo tableInfo) {// 自定义输出文件名 , 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化!!//return projectPath + "/yeb-generator/src/main/resources/mapper/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;return projectPath + "/yeb-generator/src/main/resources/mapper/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;}});cfg.setFileOutConfigList(focList);mpg.setCfg(cfg);// 配置模板TemplateConfig templateConfig = new TemplateConfig();
//执行 执行main方法,在控制台直接输出表名,多个表名用 , 隔开 结果templateConfig.setXml(null);mpg.setTemplate(templateConfig);// 策略配置StrategyConfig strategy = new StrategyConfig();//数据库表映射到实体的命名策略strategy.setNaming(NamingStrategy.underline_to_camel);//数据库表字段映射到实体的命名策略strategy.setColumnNaming(NamingStrategy.no_change);//lombok模型strategy.setEntityLombokModel(true);//生成 @RestController 控制器strategy.setRestControllerStyle(true);strategy.setInclude(scanner("表名,多个英文逗号分割").split(","));strategy.setControllerMappingHyphenStyle(true);//表前缀strategy.setTablePrefix("t_");mpg.setStrategy(strategy);mpg.setTemplateEngine(new FreemarkerTemplateEngine());mpg.execute();}
}
- 测试
运行我们编写的那个类中的main方法,然后在下面输入我们的表就可以了,下面的表的话(就是数据库里面对应的表)然后会自动生成对应的mapper文件呀
二、登录模块
(一)前提准备
1、首先导入依赖
<!-- Swagger第三方ui依赖 --><dependency><groupId>com.github.xiaoymin</groupId><artifactId>swagger-bootstrap-ui</artifactId><version>1.9.6</version></dependency><!-- swagger2 依赖 --><dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger2</artifactId><version>2.7.0</version></dependency><!--security 依赖--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency><!--JWT 依赖--><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.0</version></dependency>
2、application.properteis配置
#jwt存储的请求头
jwt.tokenHeader=Authorization
#jwt 加密使用的秘钥
jwt.secret=yeb-secret
#jwt的超限时间
jwt.expiration=604800
#jwt负载中拿到的开头
jwt.tokenHead=Bearer
3、导入一步生成的文件
(二)正式的义务逻辑实现
从接下来开始,我的建议是每一步都自己动手编写一次,不然你会后悔的
1、编写JWT类
package com.xxxx.server.config.security;import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;import java.util.Date;
import java.util.HashMap;
import java.util.Map;/*** 有生成token,判断token是否失效等方法* 如果你看不懂,建议去看我的jwt整合SpringSecurity文章的前面部分,对JWT有个大致的学习**/
@Component
public class JwtTokenUtil {//用户名的keyprivate static final String CLAIM_KEY_USERNAME = "sub";//jwt的创建时间private static final String CLAIM_KEY_CREATED = "created";//jwt秘钥@Value("${jwt.secret}")//去配置文件取得private String secret;//jwt的失效时间@Value("${jwt.expiration}")private Integer expiration;//1根据用户名生成token//2从生成的token里面拿用户名//3判断token是否失效//4判断token是否可以刷新/*** 方法一:根据用户名生成token* 根据用户信息生成对应的token* @param userDetails* @return*/public String generateToken(UserDetails userDetails) {Map<String,Object> claims = new HashMap<>();claims.put(CLAIM_KEY_USERNAME,userDetails.getUsername());//为xxx用户名生成tokenclaims.put(CLAIM_KEY_CREATED,new Date());//设置创建时间return generateToken(claims);}/*** 根据荷载生成JWT Token* @param claims* @return*/private String generateToken(Map<String,Object> claims) {return Jwts.builder().setClaims(claims)//配置的失效时间:当前时间System.currentTimeMillis().setExpiration(new Date(System.currentTimeMillis() + expiration*1000))//配置的是秘钥.signWith(SignatureAlgorithm.ES512,secret).compact();}/*** 方法二* 从token里面获取登录的用户名* @param token* @return*/public String getUserNameFromToken(String token) {String username;try {Claims claims = getClaimsFormToken(token);//调用获取荷载的方法username = claims.getSubject();//从荷载里面获取用户名} catch (Exception e) {//用户名不存在username = null;}return username;}//从token中获取荷载private Claims getClaimsFormToken(String token) {Claims claims = null;try {claims = Jwts.parser().setSigningKey(secret)//指定秘钥.parseClaimsJws(token)//指定token.getBody();} catch (Exception e) {e.printStackTrace();}return claims;}/*** 方法三:判断token是否过期* 1、判断token是否过期* 2、token荷载里面的用户名和UserDetails里面用户名是否一致* @param token* @param userDetails* @return*/public boolean validateToken(String token,UserDetails userDetails) {String username = getUserNameFromToken(token);return username.equals(userDetails.getUsername()) && !isTokenExpired(token) ;}//判断token是否失效private boolean isTokenExpired(String token) {Date expireDate = getExpiredDateFromToken(token);return expireDate.before(new Date());}private Date getExpiredDateFromToken(String token) {Claims claims = getClaimsFormToken(token);return claims.getExpiration();}/*** 方法四* 判断是否可以被刷新* @param token* @return*/public boolean canRefresh(String token) {return !isTokenExpired(token);}/*** 方法五:刷新token* @param token* @return*/public String refreshToken(String token) {Claims claims = getClaimsFormToken(token);claims.put(CLAIM_KEY_CREATED,new Date());return generateToken(claims);}}
2、返回类信息类
package com.xxxx.server.pojo;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;/*** 公共返回对象** @author zhoubin* @since 1.0.0*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class RespBean {//状态码private long code;//提示信息private String message;//返回对象private Object obj;/*** 成功返回结果* @param message* @return*/public static RespBean success(String message){return new RespBean(200,message,null);}/*** 成功返回结果* @param message* @param obj* @return*/public static RespBean success(String message,Object obj){return new RespBean(200,message,obj);}/*** 失败返回结果* @param message* @return*/public static RespBean error(String message){return new RespBean(500,message,null);}/*** 失败返回结果* @param message* @param obj* @return*/public static RespBean error(String message,Object obj){return new RespBean(500,message,obj);}
}
3、编写我们的登录专用类
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@ApiModel(value = "AdminLogin对象",description = "")
public class AdminLoginParam {@ApiModelProperty(value = "用户名",required = true)private String username;@ApiModelProperty(value = "密码",required = true)private String password;/*@ApiModelProperty(value = "验证码",required = true)private String code;*/
}
4、开始我们的登录逻辑了
(1)思路
肯定用户登录发起请求的一个任务,然后输入用户名和密码验证码和后端的进行交互,后端的处理如下:
- 编写请求的Controller,有一个登录的方法,进行请求拦截
- 编写对应的Service层,去调用我们的Mapper层,查询数据库
- 编写对应的Mapper层,去处理我们的SQL语句
- 编写对应的xml文件,查询
(2)我们的主类Admin
- 首先它的属性和我们的数据一定要匹配
- 然后要实现我们的UserDetails 接口,然后对方法进行处理
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("t_admin")
@ApiModel(value="Admin对象", description="")
public class Admin implements Serializable, UserDetails {private static final long serialVersionUID = 1L;@ApiModelProperty(value = "id")@TableId(value = "id", type = IdType.AUTO)private Integer id;@ApiModelProperty(value = "姓名")private String name;@ApiModelProperty(value = "手机号码")private String phone;@ApiModelProperty(value = "住宅电话")private String telephone;@ApiModelProperty(value = "联系地址")private String address;@ApiModelProperty(value = "是否启用")@Getter(AccessLevel.NONE)private Boolean enabled;@ApiModelProperty(value = "用户名")private String username;@ApiModelProperty(value = "密码")private String password;@ApiModelProperty(value = "用户头像")private String userFace;@ApiModelProperty(value = "备注")private String remark;@ApiModelProperty(value = "角色")@TableField(exist = false)private List<Role> roles;@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {//权限先空着,后面再来编写return null;}@Overridepublic boolean isAccountNonExpired() {return true;}@Overridepublic boolean isAccountNonLocked() {return true;}@Overridepublic boolean isCredentialsNonExpired() {return true;}@Overridepublic boolean isEnabled() {return enabled;}
}
(3)登录的Controller层
import com.xxxx.server.pojo.AdminLoginParam;
import com.xxxx.server.pojo.RespBean;
import com.xxxx.server.sevice.IAdminService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;import javax.servlet.http.HttpServletRequest;@RestController
@Api(tags = "LoginController")
public class LoginController {@Autowiredprivate IAdminService adminService;@ApiOperation(value = "登录之后返回token")@PostMapping("/login")public RespBean login(AdminLoginParam adminLoginParam, HttpServletRequest request) {return adminService.login(adminLoginParam.getUsername(),adminLoginParam.getPassword(),request);}
}
(4)Service层
每一句话都弄清楚,不懂的给我私信,我基本每天都看csdn的
/*** 登录的服务类*/
public interface IAdminService extends IService<Admin> {/*** 登录之后返回token* @param username* @param password* @param request* @return* */RespBean login(String username, String password, HttpServletRequest request);}
@Service
public class IAdminServiceImpl extends ServiceImpl<AdminMapper, Admin> implements IAdminService {@Autowiredprivate UserDetailsService userDetailsService;@AutowiredPasswordEncoder passwordEncoder;@AutowiredJwtTokenUtil jwtTokenUtil;@Value("${jwt.tokenHead")private String tokenHead;//头部信息/*** 登录之后返回token* @param username* @param password* @param request* @return*/@Overridepublic RespBean login(String username, String password, HttpServletRequest request) {//这里做一点说明,可能第一次做项目的人不是很理解,为什么这样搞//这里其实就是SpringSecurity给我提供的,如果不懂说明你SpringSecurity不行,可以去复习一下//其实就是我们编写的主类Admin实现了UserDetails和这个呼应起来,实现认证的流程UserDetails userDetails = userDetailsService.loadUserByUsername(username);if (null == userDetails || !passwordEncoder.matches(password,userDetails.getUsername())) {return RespBean.error("用户名或者密码不正确");}if (!userDetails.isEnabled()) {return RespBean.error("账号被禁用,请联系管理员");//其实这里有很多处理的,暂时这样了解}//如果上面的都通过了,说明没问题了,可以发放token令牌了String token = jwtTokenUtil.generateToken(userDetails);Map<String,String> tokenMap = new HashMap<>();tokenMap.put("token",token);//发放的tokentokenMap.put("tokenHead",tokenHead);//给前端的请求头,下次会带上//上面两步都完成了,我们编写token的时候,说了是要有时候有更新token的,我们用户也是会更新的//我们这里的更新可以用SpringSecurity提供的进行更新//第一个参数,是把我们当前的用户传入进行,第二个参数时密码,一般不放,我们自己处理,第三个是我们的权限列表UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userDetails,null,userDetails.getAuthorities());//实现更新,其实就是放入全局里面SecurityContextHolder.getContext().setAuthentication(authenticationToken);return RespBean.success("登录成功",tokenMap);}
(5)Mapper层
@Component
public interface AdminMapper extends BaseMapper<Admin> {Admin selectOne(QueryWrapper<Admin> username);
}
到现在你可以运行一下,看是不是出下面的错误
Description:Field passwordEncoder in com.xxxx.server.sevice.impl.IAdminServiceImpl required a bean of type 'org.springframework.security.crypto.password.PasswordEncoder' that could not be found.The injection point has the following annotations:- @org.springframework.beans.factory.annotation.Autowired(required=true)Action:Consider defining a bean of type 'org.springframework.security.crypto.password.PasswordEncoder' in your configuration.
原因是因为我们在编写我们的Service层的时候,里面有注入PasswordEncoder的时候,我们还没有提供,所有还会报错,慢慢的后续解决
5、退出登录和返回信息给前端
(1)首先实现思路
1、思路一
我们可以在SpringBoot里面,做相应的处理。这次不采用这种方式。
2、思路二
复习之前的思路
- 登录之后返回token。
- 前端会把token放在请求头
- 访问任何的资源都会携带我们的token进行访问。
- 然后我们去写一个拦截器,判断这个token是否合法有效的
- 如果是有效的,就允许
- 如果不对就错误
现在呢我们就和前端定义好,当你点退出登录的时候,我们返回退出成功,主要就是状态码200,拿到这个200后,前端把它的请求头中的token删除,再去调用就会被拦截。
(2)修改Controller编写
@RestController
@Api(tags = "LoginController")
public class LoginController {@Autowiredprivate IAdminService adminService;/*** 登录的请求* @param adminLoginParam* @param request* @return*/@ApiOperation(value = "登录之后返回token")@PostMapping("/login")public RespBean login(AdminLoginParam adminLoginParam, HttpServletRequest request) {return adminService.login(adminLoginParam.getUsername(),adminLoginParam.getPassword(),request);}/*** 退出登录的请求*/@ApiOperation(value = "退出登录")@PostMapping("/logout")public RespBean logout() {return RespBean.success("注销成功");}@ApiOperation(value = "获取当前登录用户信息")@GetMapping("/admin/info")public Admin getAdminInfo(Principal principal) {//这里讲一下Principal,这个是Springsecurity里面提供的,可以取对象的类,如果不知道,可以去学习一下SpringSecurity//其实刚才我们不是在编写登录方法的时候有了一个入池的操作嘛,然后我们这就可以用Principal来取if (null == principal) {return null;}String username = principal.getName();Admin admin = adminService.getAdminByUserName(username);//这里可以直接返回了,但是处于保护的情况下,设置一下admin.setPassword(null);return admin;}
}
(3)对Service进行修改
/*** 登录的服务类*/
public interface IAdminService extends IService<Admin> {/*** 登录之后返回token* @param username* @param password* @param request* @return* */RespBean login(String username, String password, HttpServletRequest request);/*** 根据用户名获取用户* @param username,名字* @return*/Admin getAdminByUserName(String username);
}
@Service
public class IAdminServiceImpl extends ServiceImpl<AdminMapper, Admin> implements IAdminService {@Autowiredprivate UserDetailsService userDetailsService;@AutowiredPasswordEncoder passwordEncoder;@AutowiredJwtTokenUtil jwtTokenUtil;@Value("${jwt.tokenHead")private String tokenHead;//头部信息@AutowiredAdminMapper adminMapper;/*** 登录之后返回token* @param username* @param password* @param request* @return*/@Overridepublic RespBean login(String username, String password, HttpServletRequest request) {//这里做一点说明,可能第一次做项目的人不是很理解,为什么这样搞//这里其实就是SpringSecurity给我提供的,如果不懂说明你SpringSecurity不行,可以去复习一下//其实就是我们编写的主类Admin实现了UserDetails和这个呼应起来,实现认证的流程UserDetails userDetails = userDetailsService.loadUserByUsername(username);if (null == userDetails || !passwordEncoder.matches(password,userDetails.getUsername())) {return RespBean.error("用户名或者密码不正确");}if (!userDetails.isEnabled()) {return RespBean.error("账号被禁用,请联系管理员");//其实这里有很多处理的,暂时这样了解}//如果上面的都通过了,说明没问题了,可以发放token令牌了String token = jwtTokenUtil.generateToken(userDetails);Map<String,String> tokenMap = new HashMap<>();tokenMap.put("token",token);//发放的tokentokenMap.put("tokenHead",tokenHead);//给前端的请求头,下次会带上//上面两步都完成了,我们编写token的时候,说了是要有时候有更新token的,我们用户也是会更新的//我们这里的更新可以用SpringSecurity提供的进行更新//第一个参数,是把我们当前的用户传入进行,第二个参数时密码,一般不放,我们自己处理,第三个是我们的权限列表UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userDetails,null,userDetails.getAuthorities());//实现更新,其实就是放入全局里面SecurityContextHolder.getContext().setAuthentication(authenticationToken);return RespBean.success("登录成功",tokenMap);}@Overridepublic Admin getAdminByUserName(String username) {//.eq("username",username)判断我们的用户是否匹配//.eq("enabled",true)判断是否用户被禁用return adminMapper.selectOne(new QueryWrapper<Admin>().eq("username",username).eq("enabled",true));}}
(4)Mapper层
mapper层不用改
6、进行Security过滤编写
(1)SecurityConfig配置
- 按着步骤看
- 不要先看,注入的东西,先去看方法,看到哪里有注入了,再去看注入的内容
package com.xxxx.server.config.security;import com.xxxx.server.pojo.Admin;
import com.xxxx.server.sevice.IAdminService;
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.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {//这个配置,是SpringBoot里面的配置,如果不懂,你也不会来做项目了,哈哈哈@Autowiredprivate IAdminService adminService;@AutowiredJwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;//权限不足@AutowiredRestfulAccessDeniedHandler restfulAccessDeniedHandler;//未登录或者token失效@AutowiredRestAuthenticationEntryPoint restAuthenticationEntryPoint;/*** 第一步看这里* @return*/@Override//不应该加个Bean就可以了吗,为什么要加Override呢,这个问题留着@Beanpublic UserDetailsService userDetailsService() {return username -> {Admin admin = adminService.getAdminByUserName(username);//实现认证都是SpringSecurity自己完成了,但是我们这自己做了处理了,就自己来解释一下//验证会走我们自己的这里,密码的话会走下面configure(AuthenticationManagerBuilder auth)配置的的passwordEncoderif (null != admin) {return admin;}return null;};}/*** 第二步来看这* @param auth* @throws Exception*/@Overridepublic void configure(AuthenticationManagerBuilder auth) throws Exception {//我们自己重写了UserDetailsService,所以要编写这这里配置一下,密码是passwordEncoder来确定的,所以去注入一下auth.userDetailsService(userDetailsService()).passwordEncoder(passwordEncoder());}@BeanPasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}/*** 第三步才是最重要的配置了* @param http* @throws Exception*/@Overridepublic void configure(HttpSecurity http) throws Exception {//使用jwt,所以我们csrf肯定不要了,所以关闭http.csrf().disable()//使用token,所以session什么的都不用了,下面这两句话可以自己去搜索看看.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()//用and继续往下面配置.authorizeRequests()//允许登录的.antMatchers("/login","logout").permitAll()//表示上面的都可以不用验证而通过.anyRequest()//.anyRequest()和.authenticated()表示其他的要认证登录.authenticated().and().headers()//这个具体自己去百度一下,反正大概是禁缓存的.cacheControl();//添加jwt,登录授权过滤器,看下面的介绍,自己编写的一个http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);//未授权和未登录的返回结果http.exceptionHandling().accessDeniedHandler(restfulAccessDeniedHandler).authenticationEntryPoint(restAuthenticationEntryPoint);}
}
(2)自定义没权限的返回
package com.xxxx.server.config.security;import com.fasterxml.jackson.databind.ObjectMapper;
import com.xxxx.server.pojo.RespBean;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;/*** 当访问接口没有权限时候,自定义返回结果*/
@Component
public class RestfulAccessDeniedHandler implements AccessDeniedHandler {@Overridepublic void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {httpServletResponse.setCharacterEncoding("UTF-8");httpServletResponse.setContentType("application/json");PrintWriter out = httpServletResponse.getWriter();RespBean bean = RespBean.error("权限不足,请联系管理员");bean.setCode(403);out.write(new ObjectMapper().writeValueAsString(bean));out.flush();out.close();}
}
(3)自定义未登录或者token失效返回页面
package com.xxxx.server.config.security;import com.fasterxml.jackson.databind.ObjectMapper;
import com.xxxx.server.pojo.RespBean;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;/*** 当未登录或者token失效时访问接口时,自定义返回结果*/
@Component
public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint {@Overridepublic void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {//这个里面主要设置上面编码的呀,还有其他的response.setCharacterEncoding("UTF-8");response.setContentType("application/json");PrintWriter out = response.getWriter();RespBean respBean = RespBean.error("未登录,请登录");respBean.setCode(401);out.write(new ObjectMapper().writeValueAsString(respBean));//这个就是向前端写一点东西out.flush();out.close();}
}
(4)自定义登录的拦截器
package com.xxxx.server.config.security;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;/*** 添加jwt,登录授权过滤器*/
@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {@Value("${jwt.tokenHeader}")private String tokenHeader;@Value("${jwt.tokenHead}")private String tokenHead;@AutowiredJwtTokenUtil jwtTokenUtil;@Autowiredprivate UserDetailsService userDetailsService;//这个是个核心的方法,需要好好理解一下,但是我也不是很懂,自己各位去网上找一找,深入的理解一下吧@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {//前端传递内容的验证String authHeader = request.getHeader(tokenHeader);//这里是前端传给我们的Authentication//判断是否存在tokenif (null != authHeader && authHeader.startsWith(tokenHead)) {String authToken = authHeader.substring(tokenHead.length());//获取一下usernameString username = jwtTokenUtil.getUserNameFromToken(authToken);//token存在用户名,但是未登录if (null != username && null == SecurityContextHolder.getContext().getAuthentication()) {UserDetails userDetails = userDetailsService.loadUserByUsername(username);//相当于登录,这里就是使用了我们的自定义的UserDetailsService里面的方法//判断一下token是否有效if (jwtTokenUtil.validateToken(tokenHead,userDetails)) {UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userDetails,"",userDetails.getAuthorities());//相当于重新设置一下,authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));SecurityContextHolder.getContext().setAuthentication(authenticationToken);}}}}
}
7、配置Swagger2
看另外一篇文章
https://blog.csdn.net/weixin_46635575/article/details/121460361
002云E办项目之登录模块相关推荐
- 2.vue3医疗在线问诊项目 - _登录模块 ==> 代码片段、css变量主题定制、cp-nav-bar组件封装、svg打包精灵图插件、cp-icon组件封装、表单校验、密码登录、短信验证码登录及两者
2.医疗在线问诊项目 - _登录模块 ==> 代码片段.css变量主题定制.cp-nav-bar组件封装.svg打包精灵图插件.cp-icon组件封装.表单校验.密码登录.短信验证码登录及两者的 ...
- 云E办项目之部门管理
文章目录 云E办---部门管理 一.展示所有部门 1. 后台接口及数据格式 2. 使用Element-ui中的Tree树形控件 3. 初始化部门信息 二. 添加部门 1. 后台接口及数据格式 2. 使 ...
- “浙里办“项目单点登录、埋点、二次回退的问题
已经有一段时间没有更新博客了,因为最近变成了某个项目的负责人,就突然忙了起来. 刚接到这个项目的开始,我还觉得自己一定能很出色的完成这个任务.刚过了几天我就发现事情并不是我想象的那么简单,我要做的事情 ...
- 客户关系管理项目——用户登录模块设计
一 模块需求细化 登录的用户,默认情况有三个不同角色,分别为:系统管理员,前台客服,信息管理员. 用户登录后能够根据其角色来进行相关工作,进行完工作需要能够注销. 细化需求如下: 用户登录之后按角色分 ...
- 【Java】云E办项目后端技术栈整合及代码阅读
项目来源:Bilibili:带你从0搭建一个springboot+vue前后端分离的java项目 源码地址:https://github.com/Jimecc/yeb 本项目的后端部分我已经完整的部署 ...
- 【代码阅读】云E办项目后端技术栈总结及源码分析
项目来源:Bilibili:带你从0搭建一个springboot+vue前后端分离的java项目 源码地址:https://github.com/Jimecc/yeb 本项目的后端部分我已经完整的部署 ...
- 亲测可用云e办项目的接口文档21年10月更新
前言 一直想开发一个功能比较强大的项目,但是一直没有动手,最近终于有点时间来折腾它了.由于时隔两年没有接触前端了,所以需要一个小项目先练练手感.等这个项目完工之后在着手搞一个大工程.都说好记星不如烂笔 ...
- 云e办(后端)——项目介绍及搭载项目
云e办 项目介绍 本项目目的是实现中小型企业的在线办公系统,云E办在线办公系统是一个用来管理日常的办公事务的一个系统,他能够管的内容有:日常的各种流程审批,新闻,通知,公告,文件信息,财务,人事,费用 ...
- vue项目云e办——登录页面(一)
云e办 文章目录 云e办 视频学习地址 前端目标 效果 登录页面代码 视频学习地址 云e办视频学习地址 前端目标 表单可以校验是否为空,如果为空,点击登录时弹出错误提示框. 效果 登录页面代码 登录页 ...
最新文章
- 三个点在同一个半圆的概率_【国际数学竞赛】列方程求概率
- Python实现字符串反转的6种方法
- 【转】PBOC3.0和PBOC2.0标准规范异同分析
- java中jtansforms,java – 使用AffineTransform旋转图像
- 男朋友的回答可以多敷衍?
- 基线检查工具_最新版CAD燕秀工具箱2.87(支持20042021)
- Oracle 10g Audit(审计) --- 记录登录用户在Oracle中的所有操作(转)
- 设计模式之 里氏替换原则
- linux 高级i o函数,高级I/O函数
- VLFeat工具包在matlab使用方法
- java编写进行货币兑换_货币汇率java assignment
- 打游戏经常有人喷,刷个B站还是有?Python实现在网站上自动评论!键盘侠都喷不赢你!
- 将.bat文件设置为Window系统开机自启动项
- 【密码学/密码分析】基于TMTO的密码分析方法
- Autosar MCAL-SPI配置及使用
- 经纬高坐标系转到东北天坐标系
- ASCII码表和常见键盘码
- AutoCAD在指定布局中如何隐藏指定的图形?
- 怎么把图片转换成Tikz图片
- 把程序作为人生,把人生当作程序