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)));
}
}
