以后直接拿着用:SpringBoot+shiro+Swagger实现前后分离的框架
2020-03-06 15:44
555 查看
文章目录
- 1、导包
- 2、封装token
- 3、编写过滤器
- 4、编写凭证匹配器
- 5、编写realm
- 6、编写Swagger的配置文件
- 7、编写shiro的配置文件
- 8、编写项目的配置文件
- 9、结果集的封装
- 9.1、接口的封装
- 9.2、返回信息码值和提示信息的封装
- 9.3、返回数据的封装
1、导包
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.54</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.4.0</version> </dependency> <!--下面导入数据库的使用的包--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.1</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <!--使用Druid这个连接池--> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.0</version> </dependency> <!--下面要导入Swagger2的相关的包--> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>2.7.0</version> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> <version>2.7.0</version> </dependency>
2、封装token
public class CustomToken extends UsernamePasswordToken { private String token; //用户身份唯一的标识 //这个token是在认证通过之后 用户访问其他资源的时候 来进行你给身份识别的 public CustomToken(String token){ this.token=token; } @Override public Object getPrincipal() { //在用户认证通过之后 再访问这个方法 默认返回的是什么? // Realm校验的第一个参数 //校验我们自己写了 还有没有第一个参数这个说法? return token; } }
3、编写过滤器
public class CustomAccessControllerFilter extends AccessControlFilter { @Override protected boolean isAccessAllowed(ServletRequest servletRequest, ServletResponse servletResponse, Object o) throws Exception { return false; } @Override protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception { HttpServletRequest request= (HttpServletRequest) servletRequest; try { //校验身份 //逻辑是什么? //第一步:获取token String token=request.getHeader(Constant.REQ_TOKEN); //第二步:看下这个token是否否为"" if(StringUtils.isEmpty(token)){ //说明你娃身份是非法的 throw new BusinessException(400001,"用户的请求的token不能为空"); }else{ //说明用户带了token //逻辑 //这里要将token进行封装 封装完了 交给shiro去做认证 看下身份是否合法 CustomToken customToken = new CustomToken(token); //记住下面这个类 在用户第一次登陆的时候 并不会执行 // 这个执行的时候 是在认证成功之后访问其他资源的 //的时候 机械能给你身份校验的 getSubject(servletRequest,servletResponse).login(customToken); } } catch (BusinessException e) { //如果是这个异常:返回JSON告诉前端出现问题了 resultResponse(e.getMessageCode(),e.getDefaultMesaage(),servletResponse); return false; } catch (AuthenticationException e) { //校验没通过的异常 // e.getCause() :返回的是当前异常的实例 if(e.getCause() instanceof BusinessException){ //表示返回的是我们自定义的异常 //将异常的实例进行转换 BusinessException err= (BusinessException) e.getCause(); resultResponse(err.getMessageCode(),err.getDefaultMesaage(),servletResponse); }else{ //如果执行到这里 说明 这个异常是shiro返回的 resultResponse(400001,"用户的认证是失败的",servletResponse); } return false; }catch (AuthorizationException e){ // e.getCause() :返回的是当前异常的实例 if(e.getCause() instanceof BusinessException){ //表示返回的是我们自定义的异常 //将异常的实例进行转换 BusinessException err= (BusinessException) e.getCause(); resultResponse(err.getMessageCode(),err.getDefaultMesaage(),servletResponse); }else{ //如果执行到这里 说明 这个异常是shiro返回的 resultResponse(403001,"用户没有访问权限",servletResponse); } return false; }catch (Exception e){ //这个分支就捕获一些未考虑的异常了 // e.getCause() :返回的是当前异常的实例 if(e.getCause() instanceof BusinessException){ //表示返回的是我们自定义的异常 //将异常的实例进行转换 BusinessException err= (BusinessException) e.getCause(); resultResponse(err.getMessageCode(),err.getDefaultMesaage(),servletResponse); }else{ //如果执行到这里 说明 这个异常是shiro返回的 resultResponse(500001,"系统出现了异常",servletResponse); } return false; } //当前的方法返回true才放行 否则这个程序也就执行到这里了.... return true; } /** * 这个方法的主要功能就是告诉前端 一些出错的信息 * @param messageCode * @param defaultMesaage * @param response */ private void resultResponse(int messageCode, String defaultMesaage, ServletResponse response) { //构建返回的数据 JSONObject jsonObject = new JSONObject(); jsonObject.put("code",messageCode); jsonObject.put("msg",defaultMesaage); //设置下返回的数据类型 response.setContentType(MediaType.APPLICATION_JSON_UTF8.toString()); //获取输出流 try { ServletOutputStream out = response.getOutputStream(); //接下来项数据写出去 out.write(jsonObject.toJSONString().getBytes()); out.flush(); } catch (IOException e) { e.printStackTrace(); } } }
4、编写凭证匹配器
public class CustomHashedCredentialsMatcher extends HashedCredentialsMatcher { @Autowired private IUserService userService; /** * 下面这个方法 返回true 或者 false就决定了 这个是成功呢还是失败 * @param token * @param info * @return */ @Override public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) { // 这里面要实现的功能很简单 // 把前面传递过来的token取出来 再把存储到服务器的token取出来做比较 // 如果一致那么就返回true 否则就返回 false CustomToken token1= (CustomToken) token; // 取出 Principal的值 (下面这个值 就是从前端传递过来进行比较的) String tokenVal= (String) token1.getPrincipal(); // 从redis 或者 数据库 或者 session取出这个信息来 //假设取出来了.... //String tokenServer="xiaoboboxiaowangzi"; boolean b=false; try{ b = userService.tokenExistsOrNot(tokenVal); }catch (Exception err){ throw new BusinessException(500001,"查询token存在失败"+err.getMessage()); } //进行比较 判定前端的token和服务端的token是否一致 如果一致 那么返回true 否则返回false if(!b){ throw new BusinessException(4010000,"授权信息无效请重新登录"); } return true; } }
5、编写realm
public class CustomRealm extends AuthorizingRealm { /** * 授权的方法 * @param principalCollection * @return */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo(); return simpleAuthorizationInfo; } /** * 认证的方法 * 将前端放进去的token 取出来 放到校验的SimpleAuthenticationInfo中去 方便后面进行校验 * token放到哪里呢? * @param authenticationToken * @return * @throws AuthenticationException */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { //取出前端传递过来的token CustomToken customToken= (CustomToken) authenticationToken; String token= (String) customToken.getPrincipal(); //这里就可以取出这个token //在这里要将前端传递过来的token给封装到 SimpleAuthenticationInfo 对象中去 SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(token,token,getName()); return simpleAuthenticationInfo; } }
6、编写Swagger的配置文件
@SpringBootConfiguration @EnableSwagger2 public class SwaggerConfig { @Bean public Docket createApi(){ return new Docket(DocumentationType.SWAGGER_2) .apiInfo(apiInfo()) .select() .apis(RequestHandlerSelectors.basePackage("com.qf.shiro.controller")) .paths(PathSelectors.any()) .build(); } /** * 页面信息的配置 * @return */ private ApiInfo apiInfo(){ return new ApiInfoBuilder() .title("springboot+shiro+swagger测试") .description("这里是整合shiro和Swagger实现前后分离") .version("v1.0") .build(); } }
7、编写shiro的配置文件
@SpringBootConfiguration public class ShiroConfig { /** * 咋们项目认证(请求资源的时候 身份的认证)的realm * @return */ @Bean public CustomRealm customRealm(){ CustomRealm customRealm = new CustomRealm(); customRealm.setCredentialsMatcher(customHashedCredentialsMatcher()); return customRealm; } /** * 凭证匹配器的申明 * @return */ @Bean public CustomHashedCredentialsMatcher customHashedCredentialsMatcher(){ return new CustomHashedCredentialsMatcher(); } /** * 安全管理器 * @return */ @Bean public DefaultWebSecurityManager securityManager(){ DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setRealm(customRealm()); return securityManager; } /** * 配置的是目标过滤器的代理 * @param securityManager * @return */ @Bean public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager securityManager){ ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); //配置安全管理器 shiroFilterFactoryBean.setSecurityManager(securityManager); //接下来配置些过滤器 //配置自己的那个过滤器 Map<String, Filter> maps=new LinkedHashMap<>(); maps.put("token",new CustomAccessControllerFilter()); shiroFilterFactoryBean.setFilters(maps); //对请求过滤和拦截的设置 Map<String,String> maps1=new LinkedHashMap<>(); //放入不拦截的页面 拦截的页面.... maps1.put("/user/login","anon"); //Swagger的所有请求的资源和请求的地址都不需要拦截 maps1.put("/swagger/**","anon"); maps1.put("/v2/api-docs","anon"); maps1.put("/swagger-ui.html","anon"); maps1.put("/swagger-resources/**","anon"); maps1.put("/webjars/**","anon"); maps1.put("/favicon.ico","anon"); maps1.put("/captcha.jpg","anon"); maps1.put("/csrf","anon"); //设置我们自己的校验 maps1.put("/**","token,authc"); shiroFilterFactoryBean.setFilterChainDefinitionMap(maps1); return shiroFilterFactoryBean; } /** * 开启aop的注解的支持 */ @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultSecurityManager securityManager){ AuthorizationAttributeSourceAdvisor attributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor(); attributeSourceAdvisor.setSecurityManager(securityManager); return attributeSourceAdvisor; } @Bean @ConditionalOnMissingBean public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator(){ DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator(); defaultAdvisorAutoProxyCreator.setProxyTargetClass(true); return defaultAdvisorAutoProxyCreator; } }
8、编写项目的配置文件
@SpringBootConfiguration @ComponentScan(basePackages = {"com.qf.shiro"}) @MapperScan(basePackages = {"com.qf.shiro.mapper"}) public class AppConfig { }
9、结果集的封装
9.1、接口的封装
public interface ResponseCodeInterface { /** * 返回的码的一个获取 * @return */ int getCode(); /** * 返回消息的获取 * @return */ String getMsg(); }
9.2、返回信息码值和提示信息的封装
public enum BaseResponseCode implements ResponseCodeInterface{ /** * 接下来就要和前端约束好所有的码值以及含义 */ SUCCESS(0,"操作成功"), SYSTEM_ERROR(5000001,"系统错误"), METHOD_INVALIDATE(4000001,"数据校验出错"), DATA_ERROR(4000002,"传入数据异常"), TOKEN_NOT_NULL(4010001,"用户认证异常"); //整个构造函数 BaseResponseCode(int code,String msg){ this.code=code; this.msg=msg; } private int code; private String msg; @Override public int getCode() { return code; } @Override public String getMsg() { return msg; } }
9.3、返回数据的封装
@Data public class DataResult <T> { private int code; //返回的码值 private String msg; //返回的错误信息提示 private T data; //返回的数据 //下面这一块是对构造器进行封装 public DataResult(int code,T data){ this.code=code; this.data=data; this.msg=null; } public DataResult(int code,String msg,T data){ this.code=code; this.data=data; this.msg=msg; } public DataResult(int code,String msg){ this.code=code; this.msg=msg; } public DataResult(){ this.code=BaseResponseCode.SUCCESS.getCode(); this.msg=BaseResponseCode.SUCCESS.getMsg(); this.data=null; } public DataResult(T data){ this.code=BaseResponseCode.SUCCESS.getCode(); this.msg=BaseResponseCode.SUCCESS.getMsg(); this.data=data; } public DataResult(ResponseCodeInterface responseCodeInterface){ this.data=null; this.code=responseCodeInterface.getCode(); this.msg=responseCodeInterface.getMsg(); } public DataResult(ResponseCodeInterface responseCodeInterface,T data){ this.data=data; this.code=responseCodeInterface.getCode(); this.msg=responseCodeInterface.getMsg(); } /** * 这个很牛逼 * 不带数据的返回 * @param <T> * @return */ public static <T>DataResult success(){ return new DataResult(); } /** * 带数据的返回 * @param data * @param <T> * @return */ public static <T>DataResult success(T data){ return new DataResult(data); } /** * 自己给参数的问题 * @param code * @param msg * @param data * @param <T> * @return */ public static <T>DataResult getResult(int code,String msg,T data){ return new <T>DataResult(code,msg,data); } /** * 自己给参数的问题 * @param code * @param msg * @param <T> * @return */ public static <T>DataResult getResult(int code,String msg){ return new <T>DataResult(code,msg); } /** * 直接传递一个枚举进来 * @param baseResponseCode * @param <T> * @return */ public static <T>DataResult getResult(BaseResponseCode baseResponseCode){ return new <T>DataResult(baseResponseCode); } /** * 直接传递一个枚举进来 * @param baseResponseCode * @param <T> * @return */ public static <T>DataResult getResult(BaseResponseCode baseResponseCode,T data){ return new <T>DataResult(baseResponseCode,data); } }
10、controller的编写
@RestController @Api(tags = {"用户接口"}) public class UserController { @Autowired private IUserService userService; private Logger logger= LoggerFactory.getLogger(UserController.class); /** * 登陆的接口 * @param user * @return */ @RequestMapping(value = "/user/login",method = RequestMethod.POST) @ApiOperation(value = "用户登陆的接口") public DataResult<User> login(@RequestBody User user){ //这个里面应该干什么? /** * 说白了 调用业务逻辑层的方法 * 异常的捕获 * 返回数据 */ DataResult<User> dataResult=null; try { User user1 = userService.login(user); dataResult=DataResult.success(user1); } catch (Exception e) { if(e instanceof BusinessException){ //说明是业务异常 BusinessException err= (BusinessException) e; //应该干什么? dataResult=new DataResult<>(err.getMessageCode(),err.getDefaultMesaage()); }else{ //dataResult=new DataResult<>(500001,"系统异常造成登陆失败"); dataResult=DataResult.getResult(BaseResponseCode.SYSTEM_ERROR.getCode(),BaseResponseCode.SYSTEM_ERROR.getMsg()); } return dataResult; } return dataResult; } /** * 查找所有的用户数据 * @return */ @RequestMapping(value = "/user/list",method = RequestMethod.GET) @ApiOperation(value = "获取所有的用户信息") @ApiImplicitParam(paramType = "header",name = "token",value = "用户token",required = true,dataType = "String") public DataResult<List<User>> findUserList(){ //定义返回数据 DataResult<List<User>> userLists; try{ //返回用户数据 List<User> users = userService.findUserList(); userLists=DataResult.success(users); logger.info("获取数据成功...."); }catch (Exception err){ //说明获取信息失败了 logger.error("获取用户信息失败:"+err.fillInStackTrace()); userLists=DataResult.getResult(BaseResponseCode.SYSTEM_ERROR.getCode(),BaseResponseCode.SYSTEM_ERROR.getMsg()); } return userLists; } }
11、Service的编写
11.1、Service接口的编写
public interface IUserService { /** * 登陆 * @param user * @return */ User login(User user); /** * 通过名字找到用户 * @param userName * @return */ User findUserByName(String userName); /** * 更新token到数据库 * @param user */ void updateToken(User user); /** * 查询所有的用户 * @return */ List<User> findUserList()throws Exception; /** * 判定这个token是否存在 * @param token * @return */ boolean tokenExistsOrNot(String token); }
11.2、Service实现的编写
@Service @Transactional public class UserService implements IUserService { @Autowired private UserMapper userMapper; @Override public User login(User user){ //这个类里面应该干什么? /**第一步:获取到前端传递过来的用户名 *第二步:通过用户名 获取用户对象 * 第三步:校验 * 第四步:生成token保存到数据库 * 第五步:将token封装到返回数据里面给前端 */ //获取用户名 String userName = user.getUserName(); //通过用户名 找用户名找对象 User userResult = findUserByName(userName); //第三步:校验 if(null==userResult){ //说明用户名不对 throw new BusinessException(40001,"用户名不对"); } //说明:用户名是对的 //比较密码 if(!(userResult.getPassword().equals(user.getPassword()))){ throw new BusinessException(40002,"密码不对"); } //执行到这里说明用户身份合法的 //先将数据保存到一个类里面 //首先要生成token这个值 String token= UUID.randomUUID().toString(); Date date=new Date(); //设置这个值给user对象 userResult.setToken(token); userResult.setExpireDate(date); //下面就是更新这个数据库的数据 updateToken(userResult); //将这个信息返回给前端 //一般情况下 密码是不需要返回的 userResult.setPassword(""); //设置返回数据的对象 //DataResult<User> userDataResult = new DataResult<>(0,"认证成功",userResult); return userResult; } @Override public User findUserByName(String userName) { return userMapper.findUserByName(userName); } @Override public void updateToken(User user) { userMapper.updateToken(user); } @Override public List<User> findUserList() throws Exception{ List<User> userList = userMapper.findUserList(); //接下来对数据进行封装 DataResult<List<User>> dataResult = new DataResult<>(0, "请求完美", userList); return userList; } @Override public boolean tokenExistsOrNot(String token) { //通过token查询用户信息 User userResult = userMapper.findUserByToken(token); //接下来就要判断了 if(null!=userResult){ return true; } return false; } }
12、Mapper接口的编写
public interface UserMapper { /** * 通过名字找到用户 * @param userName * @return */ User findUserByName(String userName); /** * 更新token到数据库 * @param user */ void updateToken(User user); /** * 查询所有的用户 * @return */ List<User> findUserList(); /** * 查看当前的token是否在数据库中存在 * @param token * @return */ User findUserByToken(String token); }
13、mapper.xml文件的编写
<?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.qf.shiro.mapper.UserMapper"> <!--通过用户名找用户--> <select id="findUserByName" parameterType="string" resultType="user"> select * from t_user where userName=#{value} </select> <!--更新数据库用户的token--> <update id="updateToken" parameterType="user"> update t_user set token=#{token} where id=#{id} </update> <!--查找所有的用户--> <select id="findUserList" resultType="user"> select * from t_user </select> <!--查看token是否存在--> <select id="findUserByToken" parameterType="String" resultType="user"> select * from t_user where token=#{value} </select> </mapper>
14、用户对象的编写
@Data @AllArgsConstructor @NoArgsConstructor public class User implements Serializable { private static final long serialVersionUID = -5199670739008077879L; private Integer id; private String userName; private String password; private String token; private Date expireDate; //token的过期时间 }
15、常量类的编写
public class Constant { public static final String REQ_TOKEN="token"; }
16、properties文件的编写
mybatis.type-aliases-package=com.qf.shiro.pojo mybatis.mapper-locations=classpath:mapper/*.xml spring.datasource.type=com.alibaba.druid.pool.DruidDataSource spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.datasource.url=jdbc:mysql:///NZ1904 spring.datasource.username=root spring.datasource.password=root
17、自定义异常的编写
public class BusinessException extends RuntimeException{ private int messageCode; private String defaultMesaage; public BusinessException(int messageCode,String defaultMesaage){ super(defaultMesaage); this.messageCode=messageCode; this.defaultMesaage=defaultMesaage; } public String getDefaultMesaage() { return defaultMesaage; } public int getMessageCode() { return messageCode; } }
希望大家关注我一波,防止以后迷路,有需要的可以加我Q讨论互相学习java ,学习路线探讨,经验分享与java Q:2415773436
- 点赞
- 收藏
- 分享
- 文章举报
相关文章推荐
- Springboot html vue.js 前后分离 跨域 Activiti6 工作流 集成代码生成器 shiro 权限
- swagger+springboot实现前(vue)后端分离
- SpringBoot2.0整合Shiro框架实现用户权限管理的示例
- Spring Boot + Spring Cloud 实现权限管理系统 后端篇(十一):集成 Shiro 框架
- 在前后端分离的SpringBoot项目中集成Shiro权限框架
- Spring Boot + Vue + Shiro 实现前后端分离、权限控制
- Spring boot整合shiro+jwt实现前后端分离
- springboot+jwt+shiro集成实现前后端分离的登录认证和拦截
- spring boot 前后端分离整合shiro(五)整合redis并实现并发登录控制
- Shiro+Springboot+Vue前后端分离实现权限管理
- spring boot配置shiro安全框架及用户登录权限验证实现
- 在前后端分离的SpringBoot项目中集成Shiro权限框架
- 搭建spring-boot+vue前后端分离框架并实现登录功能
- Spring-boot整合Swagger 实现前后端分离,前后端分离测试
- 前后分离敏捷开发框架源码基于Vue+ElementUI+Springboot后台权限
- Springboot 前后分离 vue.js html 跨域 集成代码生成器 shiro权限
- Springboot + Vue + shiro 实现前后端分离、权限控制
- springboot整合shiro-spring-boot-web-starter实现前后端分离的跨域问题
- SpringBoot 整合 Shiro 实现动态权限加载更新 + Session共享 + 单点登录
- springboot+springfox+Swagger 实现项目的restful文档的自动生成