Springboot+mybatis+security搭建个人博客网站的第一天(用户登录)
2019-04-22 22:25
375 查看
版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_42535913/article/details/89462515
很久以前就想搭建一个属于自己的博客网站。即可以提高自己的编程能力,也可以让自己的博客生产量提高一些,终于在经历了myblog1.0、2.0的版本之后,今天“删库”,重新开始搭建我的个人blog
一 所使用的技术栈
我是一名后端开发人员(在校大二学生),所以我的技术栈以后端为主。前端我是找的UI框架,现在的UI框架有很多,改一改就可以用了。以下是我使用的技术栈
工具 | 名称 |
---|---|
编译器 | idea |
编程语言 | JAVA1.8 |
数据库 | mysql 8.0 |
项目框架 | SSM |
权限控制 | spring security |
缓存 | redis |
构建工具 | Maven |
接口调试 | swagger |
二 功能需求分析
1.用户管理
- 注册
- 登录
- 修改密码
- 增加用户
- 删除用户
- 搜索用户
2.安全管理
- 角色授权
- 权限设置
3.博客管理
- 发表博客
- 编辑博客
- 删除博客
- 博客分类
- 设置标签
- 上传图片
- 模糊查询
- 最新排序
- 最热排序
- 阅读量统计
4.评论管理
- 发表评论
- 删除评论
- 统计评论数
5.点赞管理
- 点赞
- 取消点赞
- 点赞量统计
6.分类管理
- 创建分类
- 编辑分类
- 删除分类
- 按分类查询
7.标签管理
- 创建标签、
- 删除标签
- 按标签查询
8.首页搜索
- 全文检索
- 最新文章
- 最热文章(阅读量 点赞量)
- 热门标签
- 热门用户
- 热门文章
三 用户登录
数据库User表
后台对应User类
这里我是用的是mybatis自动生成代码工具 mybatis-generator:generate -e 网上教程很多,用起来也很方便 不做多余阐述
public class User implements Serializable { private Long id; private String phoneNumber; private String password; private Integer role; private String username; private String trueName; private String email; private String birthday; private String headPortrait; private static final long serialVersionUID = 1L; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getPhoneNumber() { return phoneNumber; } public void setPhoneNumber(String phoneNumber) { this.phoneNumber = phoneNumber == null ? null : phoneNumber.trim(); } public String getPassword() { return password; } public void setPassword(String password) { this.password = password == null ? null : password.trim(); } public Integer getRole() { return role; } public void setRole(Integer role) { this.role = role; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username == null ? null : username.trim(); } public String getTrueName() { return trueName; } public void setTrueName(String trueName) { this.trueName = trueName == null ? null : trueName.trim(); } public String getEmail() { return email; } public void setEmail(String email) { this.email = email == null ? null : email.trim(); } public String getBirthday() { return birthday; } public void setBirthday(String birthday) { this.birthday = birthday == null ? null : birthday.trim(); } public String getHeadPortrait() { return headPortrait; } public void setHeadPortrait(String headPortrait) { this.headPortrait = headPortrait == null ? null : headPortrait.trim(); } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(getClass().getSimpleName()); sb.append(" ["); sb.append("Hash = ").append(hashCode()); sb.append(", id=").append(id); sb.append(", phoneNumber=").append(phoneNumber); sb.append(", password=").append(password); sb.append(", role=").append(role); sb.append(", username=").append(username); sb.append(", trueName=").append(trueName); sb.append(", email=").append(email); sb.append(", birthday=").append(birthday); sb.append(", headPortrait=").append(headPortrait); sb.append(", serialVersionUID=").append(serialVersionUID); sb.append("]"); return sb.toString(); } }
登录方式采用JWT的登录方式 即JSON WEB TOKEN
- 生成token工具类
public class JwtTokenUtil { public static final long seriaVersionUID = -3301605591108950415L; static final String CLAIM_KEY_USERNAME = "sub"; static final String CLAIM_KEY_AUDIENCE = "audience"; static final String CLAIM_KEY_CREATED = "created"; private static final String AUDIENCE_UNKNOWN = "unknown"; private static final String AUDIENCE_WEB = "web"; private static final String AUDIENCE_MOBILE = "mobile"; private static final String AUDIENCE_TABLET = "tablet"; //当前的签名的秘钥 private String secret = "blog"; //token的有效时间 约25min private Long expiration = 1296000L; public String getUsernameFromToken(String token) { String username; try { final Claims claims = getClaimsFromToken(token); username = claims.getSubject(); } catch (Exception e) { username = null; } return username; } public Date getCreatDateFromToken(String token) { Date created; try { final Claims claims = getClaimsFromToken(token); created = new Date((Long) claims.get(CLAIM_KEY_CREATED)); } catch (Exception e) { created = null; } return created; } //得到token的有效期 private Date getExpirationDateFromToken(String token) { Date expiration; try { final Claims claims = getClaimsFromToken(token); expiration = claims.getExpiration(); } catch (Exception e) { expiration = null; } return expiration; } public String getAudienceFromToken(String token) { String audience; try { final Claims claims = getClaimsFromToken(token); audience = (String) claims.get(CLAIM_KEY_AUDIENCE); } catch (Exception e) { audience = null; } return audience; } private Claims getClaimsFromToken(String token) { Claims claims; try { claims = Jwts.parser() .setSigningKey(secret) .parseClaimsJws(token) .getBody(); } catch (Exception e) { claims = null; } return claims; } //设置过期时间 private Date generateExpeirationDate() { return new Date(System.currentTimeMillis() + expiration * 1000); } private Boolean isTokenExpired(String token) { final Date expiration = getExpirationDateFromToken(token); return expiration.before(new Date()); } private Boolean isCreatedAfterTenMinutes(Date created) { int minutes = (int) ((System.currentTimeMillis() - created.getTime()) / (1000 * 60)); if (minutes >= 10) { return true; } return false; } private String generateAudience(Device device) { String audience = AUDIENCE_UNKNOWN; if (device.isNormal()) { audience = AUDIENCE_WEB; } else if (device.isTablet()) { audience = AUDIENCE_TABLET; } else if (device.isMobile()) { audience = AUDIENCE_MOBILE; } return audience; } private Boolean ignoreTokenExpiration(String token) { String audience = getAudienceFromToken(token); return (AUDIENCE_TABLET.equals(audience) || AUDIENCE_MOBILE.equals(audience)); } String generateToken(Map<String, Object> claims) { return Jwts.builder() .setClaims(claims) .setExpiration(generateExpeirationDate()) .signWith(SignatureAlgorithm.HS512, secret) .compact(); } public String generateToken(UserDetails userDetails) { Map<String, Object> claims = new HashMap<>(); claims.put(CLAIM_KEY_USERNAME, userDetails.getUsername()); claims.put(CLAIM_KEY_CREATED, new Date()); return generateToken(claims); } //判断是否在10分钟后并在有效期内 public Boolean canTokenBeRefreshed(String token) { final Date created = getCreatDateFromToken(token); return token != null && created != null && isCreatedAfterTenMinutes(created) && (!isTokenExpired(token)) || ignoreTokenExpiration(token); } public String refreshToken(String token) { String refreshedToken; try { final Claims claims = getClaimsFromToken(token); claims.put(CLAIM_KEY_CREATED, new Date()); refreshedToken = generateToken(claims); } catch (Exception e) { refreshedToken = null; } log.info("获取要刷新的token: {}", refreshedToken); return refreshedToken; } public Boolean validateToken(String token, UserDetails userDetails) { JwtUser user = (JwtUser) userDetails; final String username = getUsernameFromToken(token); final Date created = getCreatDateFromToken(token); return (username.equals(user.getUsername())) && !isTokenExpired(token); } }
- 配置自己的拦截器
@Component @Slf4j public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint, Serializable { private static final long serialVersionUID = -8970718410437077606L; @Override public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException { httpServletResponse.setHeader("Access_Control_Allow_Origin","*"); httpServletResponse.setCharacterEncoding("UTF-8"); httpServletResponse.setContentType("text/html; charset=utf-8"); ResultVO result = ResultVOUtil.error(ResultEnum.AUTHENTICATION_ERROR); log.info("需要身份认证:{}" ,result); httpServletResponse.getWriter().append(JSON.toJSONString(result)); } }
@Component @Slf4j public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint, Serializable { private static final long serialVersionUID = -8970718410437077606L; @Override public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException { httpServletResponse.setHeader("Access_Control_Allow_Origin","*"); httpServletResponse.setCharacterEncoding("UTF-8"); httpServletResponse.setContentType("text/html; charset=utf-8"); ResultVO result = ResultVOUtil.error(ResultEnum.AUTHENTICATION_ERROR); log.info("需要身份认证:{}" ,result); httpServletResponse.getWriter().append(JSON.toJSONString(result)); } }
@Component @Slf4j public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint, Serializable { private static final long serialVersionUID = -8970718410437077606L; @Override public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException { httpServletResponse.setHeader("Access_Control_Allow_Origin","*"); httpServletResponse.setCharacterEncoding("UTF-8"); httpServletResponse.setContentType("text/html; charset=utf-8"); ResultVO result = ResultVOUtil.error(ResultEnum.AUTHENTICATION_ERROR); log.info("需要身份认证:{}" ,result); httpServletResponse.getWriter().append(JSON.toJSONString(result)); } }
- Security User
@Component @Slf4j public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint, Serializable { private static final long serialVersionUID = -8970718410437077606L; @Override public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException { httpServletResponse.setHeader("Access_Control_Allow_Origin","*"); httpServletResponse.setCharacterEncoding("UTF-8"); httpServletResponse.setContentType("text/html; charset=utf-8"); ResultVO result = ResultVOUtil.error(ResultEnum.AUTHENTICATION_ERROR); log.info("需要身份认证:{}" ,result); httpServletResponse.getWriter().append(JSON.toJSONString(result)); } }
@Service @Slf4j public class JwtUserDetailServiceImpl implements UserDetailsService { @Autowired private UserService userService; @Override public UserDetails loadUserByUsername(String phoneNum) throws UsernameNotFoundException { User user = userService.getUserByPhoneNum(phoneNum); if (user == null) { log.info("此用户不存在"); throw new UsernameNotFoundException(String.format("用户名为 %s 的用户不存在", phoneNum)); } else { String role = RoleEnum.getRole(user.getRole()); return new JwtUser(phoneNum, user.getPassword(), role); } } }
- WebSecurity Config
@Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint; @Autowired private UserDetailsService userDetailsService; @Autowired public void configureAuthentication(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception { authenticationManagerBuilder .userDetailsService(this.userDetailsService) .passwordEncoder(passwordEncoder()); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Bean public JwtAuthenticationTokenFilter authenticationTokenFilterBean() throws Exception { return new JwtAuthenticationTokenFilter(); } @Override @Bean public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } @Override protected void configure(HttpSecurity httpSecurity) throws Exception { httpSecurity //token的验证方式不需要开启csrf的防护 .csrf().disable() .exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint) .and() //设置无状态的连接,即不创建session .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and() .authorizeRequests() // .antMatchers(HttpMethod.OPTIONS, "/**").permitAll() // 当前的url允许进行匿名访问,即不需要身份认证 .antMatchers( "/", "/*.html", "/favicon.ico", "/**/*.html", "/**/*.css", "/**/*.js" ).permitAll() //配置swagger界面的匿名访问 .antMatchers("/swagger-ui.html").permitAll() .antMatchers("/swagger-resources/**").permitAll() .antMatchers("/images/**").permitAll() .antMatchers("/webjars/**").permitAll() .antMatchers("/v2/api-docs").permitAll() .antMatchers("/configuration/ui").permitAll() .antMatchers("/configuration/security").permitAll() //配置允许匿名访问的路径 .antMatchers("/login").permitAll() .anyRequest().authenticated(); //配置自己的验证过滤器 httpSecurity .addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class); // disable page caching httpSecurity.headers().cacheControl(); } }
- 自定义注解控制权限
@Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface RoleContro { RoleEnum role(); }
示例:
//此时只有权限为User即注册用户才有权限访问这个接口 @RoleContro(role = RoleEnum.USER) @PostMapping(name = "修改个人信息", value = "/updateUser") public void updateUser() { //TODO 用户修改or完善个人信息 }
- UserService
/** * 用户服务接口 * * @author Hobo */ public interface UserService { /** * 根据手机号码查询用户 * * @param phoneNumber * @return User * @author hobo */ public User getUserByPhoneNum(String phoneNumber); /** * 通过token解析用户 * * @return User * @author hobo */ public User getCurrentUser(); /*** * 用户登录 * @param loginForm * @param response * @author hobo * @return java.lang.Object */ public Object login(LoginForm loginForm, HttpServletResponse response); }
- UserService实现类
/** * 用户服务接口 * * @author Hobo */ public interface UserService { /** * 根据手机号码查询用户 * * @param phoneNumber * @return User * @author hobo */ public User getUserByPhoneNum(String phoneNumber); /** * 通过token解析用户 * * @return User * @author hobo */ public User getCurrentUser(); /*** * 用户登录 * @param loginForm * @param response * @author hobo * @return java.lang.Object */ public Object login(LoginForm loginForm, HttpServletResponse response); }
- loginForm
@Data public class LoginForm { @NotNull(message = "手机号不能为空") @ApiModelProperty("手机号码") private String phoneNum; @NotNull(message = "密码") private String password; }
- LoginController
@RequestMapping("/login") public class LoginController { @Autowired private UserService userService; @PostMapping(name = "用户登录", value = "/login") public Object login(LoginForm loginForm, HttpServletResponse response) { return userService.login(loginForm, response); } }
如果有志同道合的朋友或者大佬指点~ 欢迎骚扰 qq:1056024860
相关文章推荐
- spring boot + mybatis + spring security(自定义登录界面)环境搭建
- springboot+springsecurity+mybatis+thymeleaf实现用户登录和权限过程中的坑
- spring boot + mybatis + spring security(自定义登录界面)环境搭建
- SpringBoot + Mybatis + thymeleaf 搭建的个人博客
- Spring boot 搭建个人博客系统(一)——整体思路
- [置顶] 自己写了好久的SSM个人博客。。。记录下,并分享 源码----(SpringBoot+Thymeleaf+layui+mybatis)
- 个人网站的搭建(四)——利用SpringBoot发送邮件
- 使用Spring Boot搭建个人博客遇到的问题
- spring-boot集成spring-security的oauth2实现github登录网站的示例
- Spring Boot博客开发日常记录-套用Spring Security进行用户登录认证
- springboot+mybatis+SpringSecurity 实现用户角色数据库管理(一)
- springboot+mybatis+SpringSecurity 实现用户角色数据库管理(一)
- spring boot+spring security+thymeleaf在页面上判断用户是否登录
- springboot+mybatis+SpringSecurity 实现用户角色数据库管理(一)
- Spring Boot + Spring Security 防止用户在多处同时登录(一个用户同时只能登录一次)及源码分析
- 基于SpringBoot从零构建博客网站 - 整合ehcache和开发注册登录功能
- spring boot security 防止用户重复登录(原创)
- 个人网站的搭建(七)——SpringBoot集成Redis 缓存
- 用spring boot搭建一个最简单的用户登录界面,其艰难历程!
- 基于SpringBoot从零构建博客网站 - 整合lombok和mybatis-plus提高开发效率