SpringBoot第六篇-SpringBoot+Mybatis+SpringSecurity+JWT整合,開發工具IDea


1、新建SpringBoot項目,選擇Maven,插件選擇SpringWeb、SpringSecurity、Mysql Driver、Mybatis Framwork

 

 

2、創建controller,看看能否跑通,先設置配置文件application.yml的端口為8094,不喜歡用8080,因為太多程序調試使用它

server:
port: 8094
spring:
datasource:
username: root
password: root
url: jdbc:mysql://localhost/8888?characterEncoding=utf-8&useSSl=false
driver-class-name: com.mysql.jdbc.Driver
mybatis:
mapper-locations: classpath*:mappers/*Mapper.xml
type-aliases-package: cn.yinmingneng.yinmingneng.entities
configuration:
map-underscore-to-camel-case: true
@RestController
public class TestController {

    @RequestMapping("/gethello")
    public String getHello(){
        return "你好!";

    }

}

 3、輸入地址:http://localhost:8094/gethello,出現一下界面表示配置成功了

 

  用戶名:user,密碼:

 

 輸入以后登陸成功跳轉到

 

 4、創建數據庫表SysUser表,里面包含ID,username,password,rolename字段

 

 5、創建實體,放到entities包下面

package cn.yinmingneng.yinmingneng.entities;

public class SysUser {

    private  Integer id;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getRolename() {
        return rolename;
    }

    public void setRolename(String rolename) {
        this.rolename = rolename;
    }

    private  String username;
    private  String password;
    private  String rolename;
}

 

6、創建usermapper對象,用於自動生成sysuser表的增刪改查語句,此類是個接口,通過java動態代理生成,我使用配置文件生成增刪改查語句

@Mapper
public interface SysUserMapper  {

    /**
     *  添加用戶
     * @param sysUser
     * @return
     */
     int insertSysUser(SysUser sysUser);

    /**
     * 更新
     * @param sysUser
     * @return
     */
     int  updateSysUser(SysUser sysUser);

    /**
     * 刪除用戶
     * @return
     */
     int deleteSysUserById(Integer id);

     SysUser getAll(String userName);

}

7、編寫映射的SysUserMapper.XML文件,id和接口的方法名一樣,否則會報錯,映射不到

<?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="cn.yinmingneng.yinmingneng.mapper.SysUserMapper"   >



    <sql id="selectSysUser"  >
        SELECT id ,username,password,rolename FROM sysuser
    </sql>



    <select id="getAll" parameterType="String" resultType="SysUser">
        SELECT id ,username,password,rolename FROM sysuser where username = #{username}
    </select>

    <insert id="insertSysUser" parameterType="SysUser"  >
        insert into sysuser (
        <if test="username != null and username != '' ">username,</if>
        <if test="password != null and password != '' ">password,</if>
        <if test="rolename != null and rolename != '' ">config_value,</if>
        )values(
        <if test="username != null and username != ''">#{username},</if>
        <if test="password != null and password != ''">#{password},</if>
        <if test="rolename != null and rolename != ''">#{rolename},</if>
        )
    </insert>

    <update id="updateSysUser" parameterType="SysUser">
        update sysuser
        <set>
            <if test="username != null and username != ''">username = #{username},</if>
            <if test="password != null and password != ''">password = #{password},</if>
            <if test="rolename != null and rolename != ''">rolename = #{rolename}</if>
        </set>
        where id = #{id}
    </update>

    <delete id="deleteSysUserById"  parameterType="Integer">
        delete  from sysuser where id = #{id}
    </delete>


</mapper>

 8、服務層SysUserService代碼,接口,方便接口化編程

package cn.yinmingneng.yinmingneng.Services;

import cn.yinmingneng.yinmingneng.entities.SysUser;
import org.springframework.stereotype.Service;

import java.util.List;



public interface SysUserService {

    /**
     *  添加用戶
     * @param sysUser
     * @return
     */
    public int insertSysUser(SysUser sysUser);

    /**
     * 更新
     * @param sysUser
     * @return
     */
    public int  updateSysUser(SysUser sysUser);

    /**
     * 刪除用戶
     * @return
     */
    public  int deleteSysUserById();

    public SysUser getAll(String userName);
}

 9、服務層實現類,SysUserServiceImpl

package cn.yinmingneng.yinmingneng.Services.ServiceIml;

import cn.yinmingneng.yinmingneng.Services.SysUserService;
import cn.yinmingneng.yinmingneng.entities.SysUser;
import cn.yinmingneng.yinmingneng.mapper.SysUserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class SysUserServiceImpl implements SysUserService {

    @Autowired
    SysUserMapper sysUserMapper;

    @Override
    public int insertSysUser(SysUser sysUser) {
        return   sysUserMapper.insertSysUser(sysUser);
    }

    @Override
    public int updateSysUser(SysUser sysUser) {
        return sysUserMapper.updateSysUser(sysUser);
    }

    @Override
    public int deleteSysUserById() {
        return 0;
    }

    @Override
    public SysUser getAll(String userName) {

        return sysUserMapper.getAll(userName);
    }
}

10、控制器測試層,永遠測試帶權限功能,不帶權限功能,是否登錄功能,匿名訪問功能

package cn.yinmingneng.yinmingneng.Controller;

import cn.yinmingneng.yinmingneng.Services.SysUserService;
import cn.yinmingneng.yinmingneng.entities.SysUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
public class TestController {

    @Autowired
    SysUserService sysUserService;

    @RequestMapping("/gethello")
    public String getHello(){
        return "你好!";

    }
    /**
     *  添加用戶
     * @param sysUser
     * @return
     */
    @RequestMapping("/insertsysuser")
     public int insertSysUser(SysUser sysUser)
    {
         return sysUserService.insertSysUser(sysUser);
     }

    /**
     * 更新
     * @param sysUser
     * @return
     */
    @RequestMapping("/a/updatesysuser")
    @PreAuthorize("hasRole('ROLE_USER')")
    public String  updateSysUser(SysUser sysUser){
        return "sucessUpdateSysUser";
    }
    @RequestMapping("/a/getall")
   public  SysUser getAll(String userName){

        return  sysUserService.getAll(userName);
   }

}

  

11、編寫userdetailservie的實現類,該類用於實現userdetail對象,用於驗證賬號和密碼

package cn.yinmingneng.yinmingneng.config;

import cn.yinmingneng.yinmingneng.Services.SysUserService;
import cn.yinmingneng.yinmingneng.entities.SysUser;
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.User;
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.Service;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@Service
public class UserDetailimpl implements UserDetailsService {
    @Autowired
    private SysUserService sysUserService;
    /**
     * 從數據庫獲取用戶數據
     * @param s
     * @return
     * @throws UsernameNotFoundException
     */
    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {

        Collection<GrantedAuthority> authorities = new ArrayList<>();
        SysUser user = sysUserService.getAll(s);
        // 判斷用戶是否存在
        if(user == null) {
            throw new UsernameNotFoundException("用戶名不存在");
        }
        if(user.getUsername() != null && user.getUsername()!="")
        authorities.add(new SimpleGrantedAuthority(user.getRolename()));
        // 返回UserDetails實現類
        return new User(user.getUsername(), user.getPassword(), authorities);
    }
}

12、登錄認證功能,spingSecutiry提供登錄認證功能,請求/login會觸發該方法,使用post請求

package cn.yinmingneng.yinmingneng.filters;

import cn.yinmingneng.yinmingneng.Services.TokenService;
import cn.yinmingneng.yinmingneng.entities.SysUser;
import com.fasterxml.jackson.databind.deser.impl.JavaUtilCollectionsDeserializers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

/**
 * 此類用於登錄認證,有/login觸發,需要post請求
 */
public class loginfilter   extends UsernamePasswordAuthenticationFilter {
    @Autowired
    private AuthenticationManager authenticationManager;

    public loginfilter(AuthenticationManager a){
        this.authenticationManager = a;
    }
    /**
     * 驗證賬號和密碼
     * @param request
     * @param response
     * @return
     * @throws AuthenticationException
     */
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {

        String username= request.getParameter("username");
        String password = request.getParameter("password");
        //
       // return super.attemptAuthentication(request, response);
        //驗證賬號和密碼
      return    authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username,password,
              Collections.emptyList()));
    }

    /**
     * 認證成功,生成token,返回token
     * @param request
     * @param response
     * @param chain
     * @param authResult
     * @throws IOException
     * @throws ServletException
     */
    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response,
                                            FilterChain chain, Authentication authResult) throws IOException, ServletException {
     //   super.successfulAuthentication(request, response, chain, authResult);

        request.setCharacterEncoding("UTF-8");
        response.setCharacterEncoding("UTF-8");

        String username= request.getParameter("username");
        Map<String,Object> map = new HashMap<>();
     //   map.put("id",sysUser.getId());
        map.put(TokenService.userKey,username);
       // map.put(TokenService.userKey,)
        String token =  TokenService.CreateToken(map);
        response.getWriter().write("認證成功,token:"+token);
    }
}

 

13、生成token,我使用了前后端分離功能,所以需要生成token,寫了TokenService類,用於解析生成token

package cn.yinmingneng.yinmingneng.Services;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
public class TokenService {
    public static  String userKey="user_id";
    private  static  String signKey="rererrertretretrettretert";

    public static String CreateToken(Map<String, Object> map) {
        Date now = new Date(System.currentTimeMillis());
        String token = Jwts.builder()
                .setClaims(map) // 設置自定義數據
              //  .setIssuedAt(now) // 設置簽發時間
                .setExpiration(new Date(System.currentTimeMillis()+ 1000*60*60*360)) // 設置過期時間
                //.setIssuer(issuer) // 設置簽發者
               // .setSubject(subject) // 設置面向用戶
                .signWith(SignatureAlgorithm.HS512, signKey)
                .compact();
        return  token;
    }

    /**
     * 解壓token,獲取里面的令牌,
     * 自動判斷有效期
     */
    public static Map parseToken(String t) {
        try {
            return Jwts.parser()
                    .setSigningKey(signKey)
                    .parseClaimsJws(t.replace("Bearer ", ""))
                    .getBody();
        } catch (Exception e) {
            throw new IllegalStateException("Token驗證失敗:" + e.getMessage());
        }
    }
}

 13、攜帶token判斷權限,token,用於判斷token,判斷成功就寫入角色權限,該類無法自動注入SysUserService,需要手工注入,手工注入的代碼在下面

BasicAuthenticationFilter 該類繼承了oprefilter過濾器

 

package cn.yinmingneng.yinmingneng.filters;

import cn.yinmingneng.yinmingneng.Services.SysUserService;
import cn.yinmingneng.yinmingneng.Services.TokenService;
import cn.yinmingneng.yinmingneng.Utils.BeanFactoryUtil;
import cn.yinmingneng.yinmingneng.entities.SysUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
public class JwtAuthenticationFilter extends BasicAuthenticationFilter {
private static final PathMatcher pathMatcher = new AntPathMatcher();
private SysUserService sysUserService;

public JwtAuthenticationFilter(AuthenticationManager authenticationManager) {
super(authenticationManager);
}

//認證token
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {

String token = request.getHeader("Authorization");
if (token != null) {
//驗證token,並且寫入權限

Map map = TokenService.parseToken(token);
String username = (String) map.get(TokenService.userKey);
if (username != null) {

//無法自動注入,只能手工注入,不知道為啥
sysUserService= BeanFactoryUtil.getBean(SysUserService.class);
SysUser sysUser = sysUserService.getAll(username);

// 這里直接注入角色,因為JWT已經驗證了用戶合法性,所以principal和credentials直接為null即可
List<GrantedAuthority> list = new ArrayList<>();
list.add(new SimpleGrantedAuthority(sysUser.getRolename()));
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(sysUser,
null, list);

// 如果驗證失敗,設置異常;否則將UsernamePasswordAuthenticationToken注入到框架中
if (authentication == null) {
//手動設置異常
request.getSession().setAttribute("SPRING_SECURITY_LAST_EXCEPTION",
new AuthenticationCredentialsNotFoundException("權限認證失敗"));
// 轉發到錯誤Url
request.getRequestDispatcher("/login/error").forward(request, response);
} else {
SecurityContextHolder.getContext().setAuthentication(authentication);
filterChain.doFilter(request, response);
}
}
filterChain.doFilter(request, response);
}
}
}

/**
 * 手工獲取注入對象
 */

@Component
public class BeanFactoryUtil implements ApplicationContextAware {
    private static ApplicationContext context = null;

    public static <T> T getBean(Class<T> type) {
        return context.getBean(type);
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        if (BeanFactoryUtil.context == null) {
            BeanFactoryUtil.context = applicationContext;
        }
    }
}

  

 

14、springboot 接口時間映射處理,如果不處理會提示 bad request,"Bad Request",時間處理配置,此時就不用轉換date了,直接映射成date

/*
*  增加時間處理格式,否則接口無法映射時間,會報錯
* */
@Configuration
public class WebMvcConfigurerIMP implements WebMvcConfigurer {
    /**
     * 自動轉換時間格式
     *
     * @param registry date
     */
    @Override
    public void addFormatters(FormatterRegistry registry) {
        registry.addFormatter(new DateFormatter("yyyy-MM-dd HH:mm:ss"));
    }
}

15、用戶密碼是增加加密方式,注冊的時候也需要加密方式,加密方式配置,配置文件配置,注冊的時候密碼也需要增加加密

    //使用加密方式
           auth.userDetailsService(userDetailimpl).passwordEncoder(bCryptPasswordEncoder());
   @PostMapping("/insertsysuser")
     public int insertSysUser(SysUser sysUser) throws ParseException {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
      //  Date date =sdf.parse("2021-1-1 09:35:00");
        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        sysUser.setPassword( passwordEncoder.encode(sysUser.getPassword()));
      //  sysUser.setCreatetime(date);
        return sysUserService.insertSysUser(sysUser);
     }

 

16、Springboot 登錄認證失敗處理類,該類主要用於驗證失敗返回錯誤信息

@Component
public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint, Serializable
{
    private static final long serialVersionUID = -8970718410437077606L;

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e)
            throws IOException
    {
        int code = HttpStatus.UNAUTHORIZED;
        String msg = StringUtils.format("請求訪問:{},認證失敗,無法訪問系統資源", request.getRequestURI());
        ServletUtils.renderString(response, JSON.toJSONString(AjaxResult.error(code, msg)));
    }
}

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM