欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

springboot+springsecurity+mybatis+JWT+Redis?實(shí)現(xiàn)前后端離實(shí)戰(zhàn)教程

 更新時(shí)間:2024年01月22日 10:15:59   作者:一點(diǎn)點(diǎn)前進(jìn)的小鄭  
這篇文章主要介紹了springboot+springsecurity+mybatis+JWT+Redis?實(shí)現(xiàn)前后端離實(shí)戰(zhàn)教程,需要的朋友可以參考下

寫(xiě)在開(kāi)頭:這篇是實(shí)戰(zhàn)篇,即默認(rèn)各位看官具備相應(yīng)的基礎(chǔ)

一、springboot

1.新建項(xiàng)目

  我是用idea,jdk選擇1.8以上

 各個(gè)名字自行命名

添加部分依賴(lài),后面再往pom.xml加入(這里忘記改了,springboot的版本我使用的是1.5.3release版本?。?/p>

簡(jiǎn)單的項(xiàng)目搭建好了,下一步

2.application.yml的配置

數(shù)據(jù)源

spring:
  datasource:
    username: root
    password: 123456
    url: jdbc:mysql://localhost/springboot?characterEncoding=utf-8&useSSl=false
    driver-class-name: com.mysql.jdbc.Driver

數(shù)據(jù)池用了druid,pom.xml加入依賴(lài)

<!-- druid數(shù)據(jù)庫(kù)連接池 -->
<dependency>
   <groupId>com.alibaba</groupId>
   <artifactId>druid</artifactId>
   <version>1.1.8</version>
</dependency>

添加mabatis、druid,完整配置:

spring:
  datasource:
    username: root
    password: 123456
    url: jdbc:mysql://localhost/springboot?characterEncoding=utf-8&useSSl=false
    driver-class-name: com.mysql.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource
    #監(jiān)控統(tǒng)計(jì)攔截的filters
    filters: stat,wall,log4j
    #druid配置
    #配置初始化大小/最小/最大
    initialSize: 5
    minIdle: 5
    maxActive: 20
    #獲取連接等待超時(shí)時(shí)間
    maxWait: 60000
    #間隔多久進(jìn)行一次檢測(cè),檢測(cè)需要關(guān)閉的空閑連接
    timeBetweenEvictionRunsMillis: 60000
    #一個(gè)連接在池中最小生存的時(shí)間
    minEvictableIdleTimeMillis: 300000
    validationQuery: SELECT 1 FROM DUAL
    testWhileIdle: true
    testOnBorrow: false
    testOnReturn: false
    #打開(kāi)PSCache,并指定每個(gè)連接上PSCache的大小。oracle設(shè)為true,mysql設(shè)為false。分庫(kù)分表較多推薦設(shè)置為false
    poolPreparedStatements: false
    maxPoolPreparedStatementPerConnectionSize: 20
    # 通過(guò)connectProperties屬性來(lái)打開(kāi)mergeSql功能;慢SQL記錄
    connectionProperties:
      druid:
        stat:
          mergeSql: true
          slowSqlMillis: 5000
#mybatis是獨(dú)立節(jié)點(diǎn),需要單獨(dú)配置
mybatis:
  mapper-locations: classpath*:mapper/*.xml
  type-aliases-package: com.deceen.demo.entity
  configuration:
    map-underscore-to-camel-case: true

3.寫(xiě)一個(gè)小demo

controller層

package com.deceen.demo.controller;
import com.deceen.demo.entity.DemoEntity;
import com.deceen.demo.service.DemoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
 * @author: zzx
 * @date: 2018/10/25 16:48
 * @description: demo
 */
@RestController
@RequestMapping("/test")
public class DemoController {
    @Autowired
    private DemoService orderService;
    @RequestMapping("/getUser")
    public List<DemoEntity> getUser(){
        List<DemoEntity> result = orderService.getUser();
        return result;
    }
}

service層

package com.deceen.demo.service;
import com.deceen.demo.entity.DemoEntity;
import java.util.List;
/**
 * @author: zzx
 * @date: 2018/10/25 17:00
 * @description:
 */
public interface DemoService {
    List<DemoEntity> getUser();
}

impl

package com.deceen.demo.service.impl;
import com.deceen.demo.dao.DemoMapper;
import com.deceen.demo.entity.DemoEntity;
import com.deceen.demo.service.DemoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/**
 * @author: zzx
 * @date: 2018/10/25 17:03
 * @description:
 */
@Service
public class DemoServiceImpl implements DemoService {
    @Autowired
    private DemoMapper demoMapper;
    @Override
    public List<DemoEntity> getUser() {
        return demoMapper.getUser();
    }
}

dao

package com.deceen.demo.dao;
import com.deceen.demo.entity.DemoEntity;
import org.springframework.stereotype.Component;
import java.util.List;
/**
 * @author: zzx
 * @date: 2018/10/25 17:05
 * @description:
 */
@Component
public interface DemoMapper {
    List<DemoEntity> getUser();
}

entity

package com.deceen.demo.entity;
import lombok.Data;
/**
 * @author: zzx
 * @date: 2018/10/25 17:01
 * @description:
 */
@Data
public class DemoEntity {
    private Integer id;
    private Integer age;
    private String name;
    private Float height;
}

這里介紹一下lombok,感覺(jué)蠻好用的一個(gè)插件,DemoEntity這個(gè)類(lèi)中的@Data注解就是其一,可以自動(dòng)生成getset方法(編譯的時(shí)候),后面也會(huì)用到。教程:http://www.dbjr.com.cn/article/214041.htm

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.deceen.demo.dao.DemoMapper">
    <select id="getUser" resultType="com.deceen.demo.entity.DemoEntity">
        SELECT * FROM girl
    </select>
</mapper>

(相關(guān)的數(shù)據(jù)庫(kù)自己隨便建,跟DemoEntity字段對(duì)上就好了。。。)

訪(fǎng)問(wèn)http://localhost:8080/test/getUser

成功

二、druid

上文提到druid,其實(shí)尚未真正可以使用,下面我們繼續(xù)

1.druid參數(shù)配置

package com.deceen.common.config;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import javax.sql.DataSource;
/**
 * druid參數(shù)配置
 * @author zzx
 * @date 2018/10/15 10:09
 */
@Configuration
@PropertySource(value = "classpath:application.yml")
public class DruidConfiguration {
    /**
     * @author zzx
     * @date 2018-10-15 11:28
     * @todo 數(shù)據(jù)源配置
    */
    @Bean(destroyMethod = "close", initMethod = "init")
    @ConfigurationProperties(prefix = "spring.datasource")
    public DataSource druidDataSource() {
        DruidDataSource druidDataSource = new DruidDataSource();
        return druidDataSource;
    }
    /**
     * druid
     * 注冊(cè)一個(gè)StatViewServlet
     * @return
     */
    @Bean
    public ServletRegistrationBean druidStatViewServlet(){
        //org.springframework.boot.context.embedded.ServletRegistrationBean提供類(lèi)的進(jìn)行注冊(cè).
        ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new StatViewServlet(),"/druid/*");
        //添加初始化參數(shù):initParams
        //白名單:
        servletRegistrationBean.addInitParameter("allow","127.0.0.1");
        //IP黑名單 (存在共同時(shí),deny優(yōu)先于allow) : 如果滿(mǎn)足deny的話(huà)提示:Sorry, you are not permitted to view this page.
        //servletRegistrationBean.addInitParameter("deny","192.168.1.73");
        //登錄查看信息的賬號(hào)密碼.
        servletRegistrationBean.addInitParameter("loginUsername","admin");
        servletRegistrationBean.addInitParameter("loginPassword","123456");
        //是否能夠重置數(shù)據(jù).
        servletRegistrationBean.addInitParameter("resetEnable","false");
        return servletRegistrationBean;
    }
    /**
     * druid過(guò)濾器
     * 注冊(cè)一個(gè):filterRegistrationBean
     * @return
     */
    @Bean
    public FilterRegistrationBean druidStatFilter(){
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(new WebStatFilter());
        //添加過(guò)濾規(guī)則.
        filterRegistrationBean.addUrlPatterns("/*");
        //添加不需要忽略的格式信息.
        filterRegistrationBean.addInitParameter("exclusions","*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
        return filterRegistrationBean;
    }
}

訪(fǎng)問(wèn)http://localhost:8080/druid/login.html,賬號(hào)密碼為上面代碼設(shè)置的

成功

三、springsecurity

1.引入相關(guān)依賴(lài)

<!-- Spring Security -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- fastjson -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.36</version>
</dependency>

2.寫(xiě)了幾個(gè)工具類(lèi)

ResultEnum

package com.deceen.common.Enums;
import lombok.Getter;
/**
 * @author: zzx
 * @date: 2018/10/15 15:16
 * @description: 返回的錯(cuò)誤碼枚舉類(lèi)
 */
@Getter
public enum ResultEnum {
    SUCCESS(101,"成功"),
    FAILURE(102,"失敗"),
    USER_NEED_AUTHORITIES(201,"用戶(hù)未登錄"),
    USER_LOGIN_FAILED(202,"用戶(hù)賬號(hào)或密碼錯(cuò)誤"),
    USER_LOGIN_SUCCESS(203,"用戶(hù)登錄成功"),
    USER_NO_ACCESS(204,"用戶(hù)無(wú)權(quán)訪(fǎng)問(wèn)"),
    USER_LOGOUT_SUCCESS(205,"用戶(hù)登出成功"),
    TOKEN_IS_BLACKLIST(206,"此token為黑名單"),
    LOGIN_IS_OVERDUE(207,"登錄已失效"),
    ;
    private Integer code;
    private String message;
    ResultEnum(Integer code, String message) {
        this.code = code;
        this.message = message;
    }
    /**
     * @author: zzx
     * @date: 2018-10-15 16:26
     * @deprecation:通過(guò)code返回枚舉
    */
    public static ResultEnum parse(int code){
        ResultEnum[] values = values();
        for (ResultEnum value : values) {
            if(value.getCode() == code){
                return value;
            }
        }
        throw  new RuntimeException("Unknown code of ResultEnum");
    }
}

ResultVO

package com.deceen.common.VO;
import com.deceen.common.Enums.ResultEnum;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
/**
 * @author: zzx
 * @date: 2018/10/15 15:00
 * @description: 返回的格式
 */
public final class ResultVO implements Serializable {
   private static final long serialVersionUID = 1725159680599612404L;
   /**
    * 返回msg,object,以及token
    * 返回的code為默認(rèn)
    * @param message
    * @param data
    * @param jwtToken
    * @return
    */
   public final static  Map<String, Object> success(String message, Object data,String jwtToken,Boolean success) {
      Map<String, Object> map = new HashMap<>();
      map.put("jwtToken",jwtToken);
      map.put("code", ResultEnum.SUCCESS.getCode());
      map.put("message", message);
      map.put("success",success);
      map.put("data", data);
      return map;
   }
   /**
    * 返回object,以及token
    * 返回的msg,code為默認(rèn)
    * @param data
    * @param jwtToken
    * @return
    */
   public final static  Map<String, Object> success(Object data,String jwtToken) {
      Map<String, Object> map = new HashMap<String, Object>();
      map.put("jwtToken",jwtToken);
      map.put("code", ResultEnum.SUCCESS.getCode());
      map.put("message", ResultEnum.SUCCESS.getMessage());
      map.put("data", data);
      map.put("success",true);
      return map;
   }
   /**
    * 返回默認(rèn)的信息
    * @return
    */
   public final static  Map<String, Object> success() {
      Map<String, Object> map = new HashMap<String, Object>();
      map.put("jwtToken",null);
      map.put("code", ResultEnum.SUCCESS.getCode());
      map.put("message", ResultEnum.SUCCESS.getMessage());
      map.put("data", null);
      map.put("success",true);
      return map;
   }
   public final static  Map<String, Object> failure(int code, String message,Object data) {
      Map<String, Object> map = new HashMap<String, Object>();
      map.put("code", code);
      map.put("message", message);
      map.put("data", data);
      map.put("success",false);
      return map;
   }
   public final static  Map<String, Object> failure(int code, String message) {
      Map<String, Object> map = new HashMap<String, Object>();
      map.put("code", code);
      map.put("message", message);
      map.put("data", null);
      map.put("success",false);
      return map;
   }
   public final static  Map<String, Object> failure(ResultEnum respCode, Object data, Boolean success) {
      return getStringObjectMap(respCode, data,success);
   }
   public final static  Map<String, Object> failure(ResultEnum respCode, Boolean success) {
      return getStringObjectMap(respCode,success);
   }
   /*
   * 成功返回特定的狀態(tài)碼和信息
   * */
   public final static  Map<String, Object> result(ResultEnum respCode, Object data, Boolean success) {
      return getStringObjectMap(respCode, data,success);
   }
   private static  Map<String, Object> getStringObjectMap(ResultEnum respCode, Object data, Boolean success) {
      Map<String, Object> map = new HashMap<String, Object>();
      map.put("code", respCode.getCode());
      map.put("message", respCode.getMessage());
      map.put("data", data);
      map.put("success",success);
      return map;
   }
   /*
    * 成功返回特定的狀態(tài)碼和信息
    * */
   public final static  Map<String, Object> result(ResultEnum respCode, Boolean success) {
      return getStringObjectMap(respCode,success);
   }
   private static Map<String, Object> getStringObjectMap(ResultEnum respCode, Boolean success) {
      Map<String, Object> map = new HashMap<String, Object>();
      map.put("code", respCode.getCode());
      map.put("message", respCode.getMessage());
      map.put("data", null);
      map.put("success",success);
      return map;
   }
   /*
    * 成功返回特定的狀態(tài)碼和信息
    * */
   public final static Map<String, Object> result(ResultEnum respCode, String jwtToken, Boolean success) {
      Map<String, Object> map = new HashMap<String, Object>();
      map.put("jwtToken",jwtToken);
      map.put("code", respCode.getCode());
      map.put("message", respCode.getMessage());
      map.put("data", null);
      map.put("success",success);
      return map;
   }
}

可以看出來(lái),我們后面是會(huì)集成jwt的token過(guò)來(lái)的,看得懂就行,后面根據(jù)需要改

下面就是springsecurity核心幾個(gè)東西啦,具體我還沒(méi)深入,等后面有時(shí)間再寫(xiě)一篇深入一點(diǎn)的(大體分析一下shiro和springsecurity吧。。。)

3.實(shí)現(xiàn)springsecurity各個(gè)核心接口,處理用戶(hù)各種狀態(tài)

實(shí)現(xiàn)AuthenticationEntryPoint接口,處理用戶(hù)未登錄

package com.deceen.common.security;
import com.alibaba.fastjson.JSON;
import com.deceen.common.Enums.ResultEnum;
import com.deceen.common.VO.ResultVO;
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;
/**
 * @author: zzx
 * @date: 2018/10/15 15:04
 * @description: 用戶(hù)未登錄時(shí)返回給前端的數(shù)據(jù)
 */
@Component
public class AjaxAuthenticationEntryPoint implements AuthenticationEntryPoint {
    @Override
    public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
        httpServletResponse.getWriter().write(JSON.toJSONString(ResultVO.result(ResultEnum.USER_NEED_AUTHORITIES,false)));
    }
}

實(shí)現(xiàn)AccessDeniedHandler接口,處理無(wú)權(quán)登錄的情況

package com.deceen.common.security;
import com.alibaba.fastjson.JSON;
import com.deceen.common.Enums.ResultEnum;
import com.deceen.common.VO.ResultVO;
import com.deceen.rehab.user.entity.SelfUserDetails;
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;
/**
 * @author: zzx
 * @date: 2018/10/15 16:43
 * @description: 無(wú)權(quán)訪(fǎng)問(wèn)
 */
@Component
public class AjaxAccessDeniedHandler implements AccessDeniedHandler {
    @Override
    public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {
        httpServletResponse.getWriter().write(JSON.toJSONString(ResultVO.result(ResultEnum.USER_NO_ACCESS,false)));
    }
}

實(shí)現(xiàn)AuthenticationFailureHandler接口,處理用戶(hù)登錄失敗

package com.deceen.common.security;
import com.alibaba.fastjson.JSON;
import com.deceen.common.Enums.ResultEnum;
import com.deceen.common.VO.ResultVO;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
 * @author: zzx
 * @date: 2018/10/15 15:31
 * @description: 用戶(hù)登錄失敗時(shí)返回給前端的數(shù)據(jù)
 */
@Component
public class AjaxAuthenticationFailureHandler implements AuthenticationFailureHandler {
    @Override
    public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
        httpServletResponse.getWriter().write(JSON.toJSONString(ResultVO.result(ResultEnum.USER_LOGIN_FAILED,false)));
    }
}

實(shí)現(xiàn)AuthenticationSuccessHandler接口,處理登錄成功的情況

package com.deceen.common.security;
import com.alibaba.fastjson.JSON;
import com.deceen.common.Enums.ResultEnum;
import com.deceen.common.VO.ResultVO;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
 * @author: zzx
 * @date: 2018/10/15 16:12
 * @description: 用戶(hù)登錄成功時(shí)返回給前端的數(shù)據(jù)
 */
@Component
public class AjaxAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
    @Override
    public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
        SelfUserDetails userDetails = (SelfUserDetails) authentication.getPrincipal();
        String jwtToken = JwtTokenUtil.generateToken(userDetails.getUsername(), 1500);
        httpServletResponse.getWriter().write(JSON.toJSONString(ResultVO.result(ResultEnum.USER_LOGIN_SUCCESS,jwtToken,true)));
    }
}

實(shí)現(xiàn)LogoutSuccessHandler接口,處理退出成功

package com.deceen.common.security;
import com.alibaba.fastjson.JSON;
import com.deceen.common.Enums.ResultEnum;
import com.deceen.common.VO.ResultVO;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
 * @author: zzx
 * @date: 2018/10/16 9:59
 * @description: 登出成功
 */
@Component
public class AjaxLogoutSuccessHandler implements LogoutSuccessHandler {
    @Override
    public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
        httpServletResponse.getWriter().write(JSON.toJSONString(ResultVO.result(ResultEnum.USER_LOGOUT_SUCCESS,true)));
    }
}

實(shí)現(xiàn)UserDetails實(shí)現(xiàn)自定義對(duì)象

package com.deceen.demo.entity;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.io.Serializable;
import java.util.Collection;
import java.util.Set;
/**
 * @author: zzx
 * @date: 2018/10/15 16:58
 * @description: 定義user對(duì)象
 */
public class SelfUserDetails implements UserDetails, Serializable {
    private static final long serialVersionUID = 7171722954972237961L;
    private Integer id;
    private String username;
    private String password;
    private Set<? extends GrantedAuthority> authorities;
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return this.authorities;
    }
    public void setAuthorities(Set<? extends GrantedAuthority> authorities) {
        this.authorities = authorities;
    }
    @Override
    public String getPassword() { // 最重點(diǎn)Ⅰ
        return this.password;
    }
    @Override
    public String getUsername() { // 最重點(diǎn)Ⅱ
        return this.username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    //賬號(hào)是否過(guò)期
    @Override
    public boolean isAccountNonExpired() {
        return true;
    }
    //賬號(hào)是否鎖定
    @Override
    public boolean isAccountNonLocked() {
        return true;
    }
    //賬號(hào)憑證是否未過(guò)期
    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }
    @Override
    public boolean isEnabled() {
        return true;
    }
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
}

2.權(quán)限訪(fǎng)問(wèn)控制

package com.deceen.common.security;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import javax.servlet.http.HttpServletRequest;
import java.util.HashSet;
import java.util.Set;
/**
 * @author: zzx
 * @date: 2018/10/16 9:54
 * @description: 權(quán)限訪(fǎng)問(wèn)控制
 */
@Component("rbacauthorityservice")
public class RbacAuthorityService {
    public boolean hasPermission(HttpServletRequest request, Authentication authentication) {
        Object userInfo = authentication.getPrincipal();
        boolean hasPermission  = false;
        if (userInfo instanceof UserDetails) {
            String username = ((UserDetails) userInfo).getUsername();
            //獲取資源
            Set<String> urls = new HashSet();
            // 這些 url 都是要登錄后才能訪(fǎng)問(wèn),且其他的 url 都不能訪(fǎng)問(wèn)!
            urls.add("/demo/**");//application.yml里設(shè)置了項(xiàng)目路徑,百度一下我就不貼了
            Set set2 = new HashSet();
            Set set3 = new HashSet();
            AntPathMatcher antPathMatcher = new AntPathMatcher();
            for (String url : urls) {
                if (antPathMatcher.match(url, request.getRequestURI())) {
                    hasPermission = true;
                    break;
                }
            }
            return hasPermission;
        } else {
            return false;
        }
    }
}

這里提示一下啊,url記得改

3.jwt生成token的工具類(lèi)

引入依賴(lài)

<!--JWT-->
<dependency>
	<groupId>io.jsonwebtoken</groupId>
	<artifactId>jjwt</artifactId>
	<version>0.9.0</version>
</dependency>

工具類(lèi)

package com.deceen.common.utils;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.Date;
import java.util.Map;
/**
 * @author: zzx
 * @date: 2018/10/16 9:06
 * @description: jwt生成token
 */
public class JwtTokenUtil {
    // 尋找證書(shū)文件
    private static InputStream inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("jwt.jks"); // 尋找證書(shū)文件
    private static PrivateKey privateKey = null;
    private static PublicKey publicKey = null;
    static { // 將證書(shū)文件里邊的私鑰公鑰拿出來(lái)
        try {
            KeyStore keyStore = KeyStore.getInstance("JKS"); // java key store 固定常量
            keyStore.load(inputStream, "123456".toCharArray());
            privateKey = (PrivateKey) keyStore.getKey("jwt", "123456".toCharArray()); // jwt 為 命令生成整數(shù)文件時(shí)的別名
            publicKey = keyStore.getCertificate("jwt").getPublicKey();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    /**
     * 生成token
     * @param subject (主體信息)
     * @param expirationSeconds 過(guò)期時(shí)間(秒)
     * @param claims 自定義身份信息
     * @return
     */
    public static String generateToken(String subject, int expirationSeconds, Map<String,Object> claims) {
        return Jwts.builder()
                .setClaims(claims)
                .setSubject(subject)
                .setExpiration(new Date(System.currentTimeMillis() + expirationSeconds * 1000))
//                .signWith(SignatureAlgorithm.HS512, salt) // 不使用公鑰私鑰
                .signWith(SignatureAlgorithm.RS256, privateKey)
                .compact();
    }
    /**
     * @author: zzx
     * @date: 2018-10-19 09:10
     * @deprecation: 解析token,獲得subject中的信息
    */
    public static String parseToken(String token, String salt) {
        String subject = null;
        try {
            /*Claims claims = Jwts.parser()
//                    .setSigningKey(salt) // 不使用公鑰私鑰
                    .setSigningKey(publicKey)
                    .parseClaimsJws(token).getBody();*/
            subject = getTokenBody(token).getSubject();
        } catch (Exception e) {
        }
        return subject;
    }
    //獲取token自定義屬性
    public static Map<String,Object> getClaims(String token){
        Map<String,Object> claims = null;
        try {
            claims = getTokenBody(token);
        }catch (Exception e) {
        }
        return claims;
    }
    // 是否已過(guò)期
    public static boolean isExpiration(String token){
        return getTokenBody(token).getExpiration().before(new Date());
    }
    private static Claims getTokenBody(String token){
        return Jwts.parser()
                .setSigningKey(publicKey)
                .parseClaimsJws(token)
                .getBody();
    }
}

在這里展開(kāi)一下,為了實(shí)現(xiàn)前后端交互,采用了jjwt的方案,后面會(huì)加入更多的token驗(yàn)證,現(xiàn)在先把基本的東西弄出來(lái)。

大體是思路就是,每次登陸成功會(huì)返回token給前端做本地保存,以后每一次前端請(qǐng)求api都會(huì)在請(qǐng)求頭中帶上這個(gè)token,我們后面加入一個(gè)過(guò)濾器,專(zhuān)門(mén)攔截token然后驗(yàn)證。肯定會(huì)有人說(shuō)token暴露的問(wèn)題,我的解決方案很簡(jiǎn)單,實(shí)現(xiàn)一個(gè)黑名單,每一次登出或失效的token都加入黑名單(這一塊我用redis實(shí)現(xiàn),用其他緩存數(shù)據(jù)庫(kù)都行,就是一個(gè)思路的問(wèn)題)。token生成的時(shí)候也會(huì)在redis加入相應(yīng)刷新時(shí)間和失效時(shí)間(例如:7天免登陸,即在7天內(nèi)會(huì)自動(dòng)刷新用戶(hù)的token;而失效時(shí)間定為十五分鐘,即每個(gè)token只有15分鐘有效時(shí)間,過(guò)了這個(gè)時(shí)間,會(huì)去判斷是否在刷新時(shí)間內(nèi),如果是,則refresh token,并set進(jìn)request的請(qǐng)求頭之中)

參考鏈接:

https://www.cnblogs.com/stulzq/p/9678501.html#commentform(記得看評(píng)論哦,評(píng)論里也有很多精華)

https://segmentfault.com/a/1190000013151506(語(yǔ)言不同,但是思想想通)

jwt攔截器

package com.deceen.common.filters;
import com.deceen.common.utils.JwtTokenUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
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;
/**
 * @author: zzx
 * @date: 2018/10/15 17:30
 * @description: 確保在一次請(qǐng)求只通過(guò)一次filter,而不需要重復(fù)執(zhí)行
 */
@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
    @Autowired
    SelfUserDetailsService userDetailsService;
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String authHeader = request.getHeader("Authorization");
        if (authHeader != null && authHeader.startsWith("Bearer ")) {
            String authToken = authHeader.substring("Bearer ".length());
            String username = JwtTokenUtil.parseToken(authToken, "_secret");
            if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
                UserDetails userDetails = userDetailsService.loadUserByUsername(username);
                if (userDetails != null) {
                    UsernamePasswordAuthenticationToken authentication =
                                new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
                    authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                    SecurityContextHolder.getContext().setAuthentication(authentication);
                }
            }
        }
        filterChain.doFilter(request, response);
    }
}

文中的SelfUserDetailsService接下來(lái)繼續(xù)

文中的jwt.jks是jwt證書(shū),你可以自己生成,也可以用我的項(xiàng)目中的,我放到github里,在resource里找

4.springsecurity核心處理

繼承UserDetailsService,用戶(hù)認(rèn)證的業(yè)務(wù)代碼

package com.deceen.demo.service;
import com.deceen.demo.entity.SelfUserDetails;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;
import java.util.HashSet;
import java.util.Set;
/**
 * @author: zzx
 * @date: 2018/10/15 16:54
 * @description: 用戶(hù)認(rèn)證、權(quán)限
 */
@Component
public class SelfUserDetailsService implements UserDetailsService {
    @Autowired
    private UserMapper userMapper;
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //通過(guò)username查詢(xún)用戶(hù)
        SelfUserDetails user = userMapper.getUser(username);
        if(user == null){
            //仍需要細(xì)化處理
            throw new UsernameNotFoundException("該用戶(hù)不存在");
        }
        Set authoritiesSet = new HashSet();
        // 模擬從數(shù)據(jù)庫(kù)中獲取用戶(hù)角色
        GrantedAuthority authority = new SimpleGrantedAuthority("ROLE_ADMIN");
        authoritiesSet.add(authority);
        user.setAuthorities(authoritiesSet);
        log.info("用戶(hù){}驗(yàn)證通過(guò)",username);
        return user;
    }
}

相應(yīng)dao層

package com.deceen.demo.dao;
import com.deceen.demo.entity.SelfUserDetails;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Component;
/**
 * @author: zzx
 * @date: 2018/10/18 14:59
 * @description: 用戶(hù)dao層
 */
@Component
public interface UserMapper {
    //通過(guò)username查詢(xún)用戶(hù)
    SelfUserDetails getUser(@Param("username") String username);
}

相應(yīng)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.deceen.demo.dao.UserMapper">
    <select id="getUser" parameterType="String" resultType="com.deceen.demo.entity.SelfUserDetails">
        SELECT * FROM user
        where username = #{username}
    </select>
</mapper>

核心處理類(lèi)

package com.deceen.common.config;
import com.deceen.common.filters.JwtAuthenticationTokenFilter;
import com.deceen.common.security.*;
import com.deceen.demo.service.SelfUserDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
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.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
/**
 * @author: zzx
 * @date: 2018/10/15 16:47
 * @description:
 */
@Configuration
public class SpringSecurityConf extends WebSecurityConfigurerAdapter {
    @Autowired
    AjaxAuthenticationEntryPoint authenticationEntryPoint;//未登陸時(shí)返回 JSON 格式的數(shù)據(jù)給前端(否則為 html)
    @Autowired
    AjaxAuthenticationSuccessHandler authenticationSuccessHandler; //登錄成功返回的 JSON 格式數(shù)據(jù)給前端(否則為 html)
    @Autowired
    AjaxAuthenticationFailureHandler authenticationFailureHandler; //登錄失敗返回的 JSON 格式數(shù)據(jù)給前端(否則為 html)
    @Autowired
    AjaxLogoutSuccessHandler logoutSuccessHandler;//注銷(xiāo)成功返回的 JSON 格式數(shù)據(jù)給前端(否則為 登錄時(shí)的 html)
    @Autowired
    AjaxAccessDeniedHandler accessDeniedHandler;//無(wú)權(quán)訪(fǎng)問(wèn)返回的 JSON 格式數(shù)據(jù)給前端(否則為 403 html 頁(yè)面)
    @Autowired
    SelfUserDetailsService userDetailsService; // 自定義user
    @Autowired
    JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter; // JWT 攔截器
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        // 加入自定義的安全認(rèn)證
//        auth.authenticationProvider(provider);
        auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder());
    }
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 去掉 CSRF
        http.csrf().disable()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) // 使用 JWT,關(guān)閉token
                .and()
                .httpBasic().authenticationEntryPoint(authenticationEntryPoint)
                .and()
                .authorizeRequests()//定義哪些URL需要被保護(hù)、哪些不需要被保護(hù)
                .anyRequest()//任何請(qǐng)求,登錄后可以訪(fǎng)問(wèn)
                .access("@rbacauthorityservice.hasPermission(request,authentication)") // RBAC 動(dòng)態(tài) url 認(rèn)證
                .and()
                .formLogin()  //開(kāi)啟登錄, 定義當(dāng)需要用戶(hù)登錄時(shí)候,轉(zhuǎn)到的登錄頁(yè)面
//                .loginPage("/test/login.html")
//                .loginProcessingUrl("/login")
                .successHandler(authenticationSuccessHandler) // 登錄成功
                .failureHandler(authenticationFailureHandler) // 登錄失敗
                .permitAll()
                .and()
                .logout()//默認(rèn)注銷(xiāo)行為為logout
                .logoutUrl("/logout")
                .logoutSuccessHandler(logoutSuccessHandler)
                .permitAll();
        // 記住我
        http.rememberMe().rememberMeParameter("remember-me")
                .userDetailsService(userDetailsService).tokenValiditySeconds(1000);
        http.exceptionHandling().accessDeniedHandler(accessDeniedHandler); // 無(wú)權(quán)訪(fǎng)問(wèn) JSON 格式的數(shù)據(jù)
        http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class); // JWT Filter
    }
}

有個(gè)點(diǎn)說(shuō)一下:我密碼加密選擇了BCryptPasswordEncoder格式,官方也推薦這個(gè),然后你們可以自行建一個(gè)測(cè)試用戶(hù),記得密碼先用BCryptPasswordEncoder加密一下哦(寫(xiě)一個(gè)createUser方法,密碼加密保存)

測(cè)試

postman模擬請(qǐng)求:

登錄成功:

復(fù)制token,如下操作,進(jìn)行接口測(cè)試:

請(qǐng)求接口成功:

下一篇講解集成redis+token刷新+logback,有興趣就接著看吧

本篇文章的代碼:https://github.com/zzxzzxhao/springboot-springsecurity

springboot+springsecurity+mybatis+JWT+Redis 實(shí)現(xiàn)前后端離(實(shí)戰(zhàn)篇續(xù))

后記:

1.加入簡(jiǎn)單注冊(cè)功能,SpringSecurityConf加入配置放開(kāi)url,DemoController加入如下代碼

/**
 * 簡(jiǎn)單注冊(cè)功能
 * @param username
 * @param password
 * @return
 */
@PostMapping("/register")
public Map<String, Object> register(String username,String password){
	orderService.register(username,password);
	return ResultVO.result(ResultEnum.SUCCESS,true);
}

(詳細(xì)代碼我上傳到github了)

2.關(guān)于mysql新版本,導(dǎo)致mysql-connector-java版本對(duì)應(yīng)不上的問(wèn)題

mysql版本:

pom.xml更新對(duì)應(yīng)mysql-connector-java版本

<dependency>
	<groupId>mysql</groupId>
	<artifactId>mysql-connector-java</artifactId>
	<version>8.0.11</version>
</dependency>

同時(shí)配置文件中關(guān)于數(shù)據(jù)庫(kù)連接地址需要加上&serverTimezone=GMT%2B8,即:

spring:
  datasource:
    username: root
    password: 123
    url: jdbc:mysql://localhost/springboot?characterEncoding=utf-8&useSSl=false&serverTimezone=GMT%2B8
    driver-class-name: com.mysql.jdbc.Driver

3.關(guān)于自定義登錄的

其實(shí)也很簡(jiǎn)單,但是我就不上傳了,簡(jiǎn)單說(shuō)一下spring security流程

這個(gè)流程很清楚啦

UsernamePasswordAuthenticationToken封裝加密后的密碼以及用戶(hù)信息

BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, bCryptPasswordEncoder.encode(password));
try {
	Authentication authentication = authenticationManager.authenticate(authenticationToken);
	SecurityContextHolder.getContext().setAuthentication(authentication);
	//具體實(shí)現(xiàn)。。。
} catch (AuthenticationException e) {
	//自行處理
}

到此這篇關(guān)于springboot+springsecurity+mybatis+JWT+Redis 實(shí)現(xiàn)前后端離實(shí)戰(zhàn)教程的文章就介紹到這了,更多相關(guān)springboot+springsecurity+mybatis+JWT+Redis 前后端離內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Spring?Boot使用HMAC-SHA256對(duì)訪(fǎng)問(wèn)密鑰加解密

    Spring?Boot使用HMAC-SHA256對(duì)訪(fǎng)問(wèn)密鑰加解密

    本文主要介紹了使用HMAC-SHA256算法進(jìn)行客戶(hù)端和服務(wù)端之間的簽名驗(yàn)簽,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2024-12-12
  • Java多線(xiàn)程Thread及其原理詳解

    Java多線(xiàn)程Thread及其原理詳解

    這篇文章主要介紹了Java多線(xiàn)程Thread及其原理,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2025-05-05
  • logback ThresholdFilter臨界值日志過(guò)濾器源碼解讀

    logback ThresholdFilter臨界值日志過(guò)濾器源碼解讀

    這篇文章主要為大家介紹了logback ThresholdFilter臨界值日志過(guò)濾器源碼解讀,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-11-11
  • Java用鄰接表存儲(chǔ)圖的示例代碼

    Java用鄰接表存儲(chǔ)圖的示例代碼

    鄰接表是圖的一種鏈?zhǔn)酱鎯?chǔ)方法,其數(shù)據(jù)結(jié)構(gòu)包括兩部分:節(jié)點(diǎn)和鄰接點(diǎn)。本文將用鄰接表實(shí)現(xiàn)存儲(chǔ)圖,感興趣的小伙伴可以了解一下
    2022-06-06
  • 詳解Spring如何解析占位符

    詳解Spring如何解析占位符

    Spring一直支持將屬性定義到外部的屬性的文件中,并使用占占位符的形式為使用"${}"包裝的屬性名稱(chēng),為了使用屬性占位符,我們必須配置一個(gè)PropertyPlaceholderConfigurer或PropertySourcesPlaceholderConfigurer實(shí)例,本文將介紹如何解析占位符
    2021-06-06
  • Springboot項(xiàng)目參數(shù)校驗(yàn)方式(Validator)

    Springboot項(xiàng)目參數(shù)校驗(yàn)方式(Validator)

    本文介紹了如何在Spring Boot項(xiàng)目中使用`spring-boot-starter-validation`包和注解來(lái)實(shí)現(xiàn)請(qǐng)求參數(shù)校驗(yàn),主要介紹了校驗(yàn)注解的使用方法、校驗(yàn)失敗的異常捕獲以及`@Validated`的分組功能
    2025-02-02
  • java?String拼接json的方式實(shí)現(xiàn)

    java?String拼接json的方式實(shí)現(xiàn)

    本文主要介紹了java?String拼接json的方式,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-09-09
  • linux系統(tǒng)下java項(xiàng)目在后臺(tái)啟動(dòng)的4種方式總結(jié)

    linux系統(tǒng)下java項(xiàng)目在后臺(tái)啟動(dòng)的4種方式總結(jié)

    Linux是集多種功能于一身的操作系統(tǒng),它可以讓用戶(hù)查看和管理當(dāng)下正在運(yùn)行的進(jìn)程,包括Java程序,這篇文章主要給大家總結(jié)介紹了關(guān)于linux系統(tǒng)下java項(xiàng)目在后臺(tái)啟動(dòng)的4種方式,需要的朋友可以參考下
    2023-10-10
  • 如何實(shí)現(xiàn)廣告彈窗觸達(dá)頻率的控制?

    如何實(shí)現(xiàn)廣告彈窗觸達(dá)頻率的控制?

    這篇文章主要介紹了如何實(shí)現(xiàn)廣告彈窗觸達(dá)頻率的控制,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-04-04
  • IDEA2020.2創(chuàng)建springboot項(xiàng)目卡死在reading maven project的問(wèn)題

    IDEA2020.2創(chuàng)建springboot項(xiàng)目卡死在reading maven project的問(wèn)題

    這篇文章主要介紹了關(guān)于2020.2IDEA用spring Initializr創(chuàng)建maven的springboot項(xiàng)目卡死在reading maven project的問(wèn)題描述及解決方法,感興趣的朋友跟隨小編一起看看吧
    2020-09-09

最新評(píng)論