博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
SpringBoot2.1版本的个人应用开发框架 - 整合vue实现前后端分离
阅读量:7029 次
发布时间:2019-06-28

本文共 11309 字,大约阅读时间需要 37 分钟。

本篇作为SpringBoot2.1版本的个人开发框架 子章节,请先阅读再次阅读本篇文章

项目地址:

感谢大神给我们创建了这么好的vue后端管理模板,大神有一系列的教程,在预览地址中有系列文章的地址,还有项目github的地址,感觉大神就是帅气。

  • vue-element-admin预览地址:

  • vue官方文档:

  • node安装:

下载PanJiaChen大神的,这个是大神推荐的二次开发的模板,vue-element-admin大神希望是一个集成方案,当我们需要什么再去拿什么。

这里我不对项目结构做介绍,在大神的系列文章中都有介绍,当我们把项目下载好以后,我们尝试的在本地跑起来,确定没有错误以后我们再进行下一步。

启动初始vue-admin-template项目

在下载好的项目中运行cmd,先下载项目所需要的依赖后再启动

npm install。。。。npm run dev复制代码

效果图,现在登陆的还是默认的用户,我们要实现的功能是:前端与后端做交互,并在数据库中查询用户时候否是有权限登陆。

跑起来后登陆的界面如上图,没有预览的功能多,所以以后我们按照我们自己想要的需求一一加进去。

后端项目

我们想要后端与前端交互起来,其实还是需要修改挺多地方的,这里先介绍修改后端,在前后端分离的项目中多数用Token来做请求的认证,我也是实现了jwt和SpringSecurity来保护API,他们俩在我理解来看是没有直接关系的,而是合作的关系,由SpringSecurity来决定什么请求可以访问我们服务器,可以访问的请求再由jwt来判断是否携带Token,没有携带的不予通过,再加上网上很多都是通过这种模式来实现的,参考的资料也比较多。

推荐:

jwt的创建

在security模块中的application-security.yml文件中添加以下内容,jwt的加密字符串是一个提前写好的,这里就相当于配置了三个常量,并没有什么特别的,之后会在类中加载,如果闲麻烦,可以直接在类中定义常量即可。

jwt:  header: token   #jwt的请求头  secret: eyJleHAiOjE1NDMyMDUyODUsInN1YiI6ImFkbWluIiwiY3Jl   #jwt的加密字符串  expiration: 3600000   #jwt token有效时间(毫秒)一个小时复制代码

在ywh-starter-security模块的utils包中创建工具类,如果想看详细的代码,可以前往我的GitHub查看详细代码。

package com.ywh.security.utils;/** * CreateTime: 2019-01-22 10:27 * ClassName: JwtTokenUtil * Package: com.ywh.security.utils * Describe: * jwt的工具类 * * @author YWH */@Data@Component@ConfigurationProperties(prefix = "jwt")public class JwtTokenUtil {    private String secret;    private Long expiration;    private String header;    /**     * 从数据声明生成令牌     *     * @param claims 数据声明     * @return 令牌     */    private String generateToken(Map
claims) { Date expirationDate = new Date(System.currentTimeMillis() + expiration); return Jwts.builder() .setClaims(claims) .setExpiration(expirationDate) .signWith(SignatureAlgorithm.HS256, secret) .compact(); } /** * 生成令牌 * @return 令牌 */ public String generateToken(String userName) { Map
claims = new HashMap<>(2); claims.put("sub", userName); claims.put("created", new Date()); return generateToken(claims); } 。。。。。。。。。。。。中间省略了代码}复制代码

在我们前端向后端请求时,我们要每一次的判断是否携带了token,这个任务我们就交给拦截器来执行,创建拦截器

package com.ywh.security.filter;/** * CreateTime: 2019-01-29 18:15 * ClassName: JwtAuthenticationTokenFilter * Package: com.ywh.security.filter * Describe: * spring的拦截器 * * @author YWH */@Componentpublic class JwtAuthenticationTokenFilter extends OncePerRequestFilter {    private final static Logger log = LoggerFactory.getLogger(JwtAuthenticationTokenFilter.class);    private JwtTokenUtil jwtTokenUtil;    private UserDetailsService userDetailsService;    @Autowired    public JwtAuthenticationTokenFilter(JwtTokenUtil jwtTokenUtil, UserDetailsService userDetailsService) {        this.userDetailsService = userDetailsService;        this.jwtTokenUtil = jwtTokenUtil;    }    /**     * 该拦截器主要的功能是,拦截请求后,判断是否携带token,如果未携带token则不予通过。     * @param httpServletRequest http请求     * @param httpServletResponse http响应     * @param filterChain 拦截器     * @throws ServletException 异常信息     * @throws IOException 异常信息     */    @Override    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {        // 获取request中jwt token        String authHeader = httpServletRequest.getHeader(jwtTokenUtil.getHeader());        // 验证token是否存在        if(StringUtils.isNotEmpty(authHeader)){            //根据token获取用户名            String userName = jwtTokenUtil.getUsernameFromToken(authHeader);            if(userName != null && SecurityContextHolder.getContext().getAuthentication() == null){                // 通过用户名 获取用户的信息                UserDetails userDetails = userDetailsService.loadUserByUsername(userName);                // 验证token和用户信息是否匹配                if(jwtTokenUtil.validateToken(authHeader,userDetails)){                    // 然后构造UsernamePasswordAuthenticationToken对象                    // 最后绑定到当前request中,在后面的请求中就可以获取用户信息                    UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());                    authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpServletRequest));                    SecurityContextHolder.getContext().setAuthentication(authentication);                }            }        }        filterChain.doFilter(httpServletRequest, httpServletResponse);    }}复制代码

拦截器写好以后,我们需要修改类中configure(HttpSecurity httpSecurity)方法,这个类在我上两篇文章中都有介绍,以下代码中我写了跨域请求的后端实现。后面我们就不用在前端实现跨域请求的设置了,不过我也会把前端如何实现跨域写出来的。

/**     * 配置如何通过拦截器保护我们的请求,哪些能通过哪些不能通过,允许对特定的http请求基于安全考虑进行配置     * @param httpSecurity http     * @throws Exception 异常     */    @Override    protected void configure(HttpSecurity httpSecurity) throws Exception {        httpSecurity                // 暂时禁用csrc否则无法提交                .csrf().disable()                // session管理                .sessionManagement()                // 我们使用SessionCreationPolicy.STATELESS无状态的Session机制(即Spring不使用HTTPSession),对于所有的请求都做权限校验,                // 这样Spring Security的拦截器会判断所有请求的Header上有没有”X-Auth-Token”。                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)                // 设置最多一个用户登录,如果第二个用户登陆则第一用户被踢出,并跳转到登陆页面                .maximumSessions(1).expiredUrl("/login.html");        httpSecurity                // 开始认证                .authorizeRequests()                // 对静态文件和登陆页面放行                .antMatchers("/static/**").permitAll()                .antMatchers("/auth/**").permitAll()                .antMatchers("/login.html").permitAll()                // 其他请求需要认证登陆                .anyRequest().authenticated();        // 注入我们刚才写好的 jwt过滤器,添加在UsernamePasswordAuthenticationFilter过滤器之前        httpSecurity.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);        // 这块是配置跨域请求的         ExpressionUrlAuthorizationConfigurer
.ExpressionInterceptUrlRegistry registry = httpSecurity.authorizeRequests(); // 让Spring security放行所有preflight request registry.requestMatchers(CorsUtils::isPreFlightRequest).permitAll(); } /** * 这块是配置跨域请求的 * @return Cors过滤器 */ @Bean public CorsFilter corsFilter() { final UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource(); final CorsConfiguration cors = new CorsConfiguration(); cors.setAllowCredentials(true); cors.addAllowedOrigin("*"); cors.addAllowedHeader("*"); cors.addAllowedMethod("*"); urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", cors); return new CorsFilter(urlBasedCorsConfigurationSource); }复制代码

可以看到上面代码中,我把我们实现的拦截器放到了SpringSecurity的拦截器链中去了,这就使他们俩有了合作的关系,接下来就是创建我们的和,实现最基本的登陆和退出,用户登陆后返回一个Token,前端存在本地缓存(localStorage)或者sessionStorage中,以供之后的请求使用。

package com.ywh.security.service.impl;/** * CreateTime: 2019-01-25 * ClassName: SysUserServiceImpl * Package: com.ywh.security.service.impl * Describe: *  业务逻辑接口的实现类 * @author YWH */@Servicepublic class SysUserServiceImpl extends BaseServiceImpl
implements SysUserService { private static final Logger log = LoggerFactory.getLogger(SysUserServiceImpl.class); @Autowired private SysUserDao dao; @Autowired private AuthenticationManager authenticate; @Autowired private JwtTokenUtil jwtTokenUtil; /** * 获取用户详细信息 * @param username 用户名 * @return 实体类 */ @Override public SysUserEntity findUserInfo(String username) { return dao.selectByUserName(username); } /** * 用户登陆 * @param username 用户名 * @param password 密码 * @return 登陆成功 返回token */ @Override public String login(String username, String password) throws AuthenticationException { // 内部登录请求 UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password); // 验证是否有权限 Authentication auth = authenticate.authenticate(authRequest); log.debug("===============权限============" + auth); SecurityContextHolder.getContext().setAuthentication(auth); return jwtTokenUtil.generateToken(username); }}复制代码

Controller的实现,因为都是最简单的实现,可以根据自己的需求修改,后期也可以再根据自己的想法加相应的实现即可。

package com.ywh.security.controller;/** * CreateTime: 2019-01-28 16:06 * ClassName: AuthController * Package: com.ywh.security.controller * Describe: * 权限控制器 * * @author YWH */@RestController@RequestMapping("auth")public class AuthController {    private static final Logger LOG = LoggerFactory.getLogger(AuthController.class);    @Autowired    private SysUserService sysUserService;    /**     * 登陆     * @param map 接收体     * @return 返回token     */    @PostMapping("login")    public Result login(@RequestBody Map
map){ try { String token = sysUserService.login(map.get("username"), map.get("password")); return Result.successJson(token); }catch (AuthenticationException ex){ LOG.error("登陆失败",ex); return Result.errorJson(BaseEnum.PASSWORD_ERROR.getMsg(),BaseEnum.PASSWORD_ERROR.getIndex()); } } /** * 用户详情 * @return 用户详细信息 */ @Cacheable(value = "userInfo") @GetMapping("userInfo") public Result userInfo(){ Object authentication = SecurityContextHolder.getContext().getAuthentication().getPrincipal(); if(authentication instanceof SecurityUserDetails){ return Result.successJson(sysUserService.findUserInfo(((SecurityUserDetails) authentication).getUsername())); } return Result.errorJson(BaseEnum.LOGIN_AGIN.getMsg(),BaseEnum.LOGIN_AGIN.getIndex()); } @PostMapping("logOut") public Result logOut(){ return Result.successJson("退出成功,因为token本身是无状态,如果通过redis来控制token的生存周期,则变成了有状态,所以暂时没有好的解决办法。"); }}复制代码

到此就结束了后端项目的修改,我觉的最重要的是要明白它们是怎么样的工作流程,知道流程后我们就好理解很多,一步一步往下写就可以了,碰到不会的多Google多百度即可。

jwt和SpringSecurity的流程总结:

  • 先要有一个可以生成Token的工具类。
  • 实现jwt的拦截器,判断每一次请求是否携带Token。
  • 修改Security的配置类,使jwt和Security联系起来,有合作关系。
  • 创建service实现最简单的登陆以及查询用户等操作。
  • 创建Controller,提供前端所要的接口。

前端项目

在上面我们把前端项目vue-elment-template跑起来后,需要修改挺多地方的,比较杂,我也是遇到一个错误修改一个地方。

在后端项目中我已经实现了后端跨域的方法,但是前端也是可以实现跨域请求的,两者选择哪个都可以。

  • vue-element-template跨域问题,需要把src/utils/request.js中的baseURL的地址去掉,然后在配置/config/index.js中的proxyTable 解决跨域问题。
proxyTable: {  '/core': {    target: 'http://192.168.0.117:8082', // 接口的域名    // secure: false,  // 如果是https接口,需要配置这个参数    changeOrigin: true, // 如果接口跨域,需要进行这个参数配置    pathRewrite: {      '^/core': '/core'    }  }},复制代码
  • config\dev.env.js和config\prod.env.js 修改访问根路径
'use strict'module.exports = {  NODE_ENV: '"production"',  BASE_API: '"http://localhost:8082/core/"',}复制代码
  • src\api\login.js 解决访问路径问题,由于我们controller中的路径使auth/**,所以要修改成我们自己的路径,我只写一个示例,剩下的按着修改。
export function login(username, password) {  return request({    url: '/auth/login',    method: 'post',    data: {      username,      password    }  })}复制代码
  • src\utils\request.js 修改token名字,这个就是修改Header头中携带的Token名字,这个是后端决定的,我们在前面的yml文件中定义的什么这里就写什么,还有就是状态码等。

  • src\store\modules\user.js 修改登陆等问题,在这里我们要修改的比较多一点,语言描述也不太好描述,我就简略劫了两张图,如果遇到错误自己解决掉正好多熟悉一下。

以上差不多就是我在前端遇到的大问题,很有很多小问题,就不一一贴了,设置了以上后,可以先试一试能不能跑起来,如果不行,可以对比我在GitHub的代码。

效果图

当我们再次登陆时,可以看到我们已经是在数据库中查询用户信息并且登陆了。

如果再以默认的admin登陆则显示用户名或密码错误

转载于:https://juejin.im/post/5caf0e53e51d456e4a411e27

你可能感兴趣的文章
红帽论坛北京站召开 设立亚太开放创新实验室
查看>>
苏宁11.11:如何基于异步化打造会员任务平台?
查看>>
区块链和数据科学:如果同时应用这两种技术,将会实现什么?
查看>>
Oracle即将发布的全新Java垃圾收集器 ZGC
查看>>
深入浅出Tensorflow(三):训练神经网络模型的常用方法
查看>>
Blazor将.NET带回到浏览器
查看>>
利用人工智能提升团队包容性
查看>>
详解分布式系统本质:“分治”和“冗余”
查看>>
gRPC-Web发布,REST又要被干掉了?
查看>>
全站爬虫项目一阶段总结
查看>>
在项目中引入领域驱动设计的经验
查看>>
用关系型NoSQL回到未来
查看>>
Jeff Bean谈Flink与流式处理的5大新发现
查看>>
技术寡头争霸传之:控制开源工具,就控制了整个生态
查看>>
微软把UWP定位成业务线应用程序开发平台
查看>>
2018腾讯云+未来峰会互联网专场:腾讯云智能物联解决方案亮相
查看>>
Python数据可视化的10种技能
查看>>
关于有效的性能调优的一些建议
查看>>
微软发起Java on Azure调查,呼吁Java社区积极参与
查看>>
搭建svn仓库
查看>>