配置信息
# ServerProperties
server.port=8080
server.servlet.context-path=/community
# ThymeleafProperties
spring.thymeleaf.cache=false
# DataSourceProperties
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/community?characterEncoding=utf-8&useSSL=false&serverTimezone=Hongkong
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.type=com.zaxxer.hikari.HikariDataSource
spring.datasource.hikari.maximum-pool-size=15
spring.datasource.hikari.minimum-idle=5
spring.datasource.hikari.idle-timeout=30000
# MybatisProperties
#mapper类和mapper.xml不在同一个路径下时,指定mapper.xml的路径
mybatis.mapper-locations=classpath:mapper/*.xml
#取别名
mybatis.type-aliases-package=com.nowcoder.community.entity
#如果插入的表以自增列为主键,则允许 JDBC 支持自动生成主键,并可将自动生成的主键返回
mybatis.configuration.useGeneratedKeys=true
#自动转换驼峰命名
mybatis.configuration.mapUnderscoreToCamelCase=true
# MailProperties
# 访问邮箱的域名 smtp表示协议
spring.mail.host=smtp.qq.com
spring.mail.username=****@qq.com
#授权码
spring.mail.password=*****
spring.mail.protocol=smtps
# 采用ssl安全连接
spring.mail.properties.mail.smtp.ssl.enable=true
# 自定义KV
community.path.domain=http://localhost:8080
实体类
public class User {
private int id;
private String username;
private String password;
private String salt;
private String email;
private int type;
private int status;
private String activationCode;
private String headerUrl;
private Date createTime;
}
密码加密工具类
public class CommunityUtil {
public static String generateUUID ( ) {
return UUID. randomUUID ( ) . toString ( ) . replaceAll ( "-" , "" ) ;
}
public static String md5 ( String key) {
if ( StringUtils. isBlank ( key) ) {
return null;
}
return DigestUtils. md5DigestAsHex ( key. getBytes ( ) ) ;
}
}
Mapper层
dao接口
@Mapper
public interface UserMapper {
User selectById ( int id) ;
User selectByName ( String username) ;
User selectByEmail ( String email) ;
int insertUser ( User user) ;
int updateStatus ( int id, int status) ;
int updateHeader ( int id, String headerUrl) ;
int updatePassword ( int id, String password) ;
}
mapper.xml
< mapper namespace = " com.nowcoder.community.dao.UserMapper" >
< sql id = " insertFields" >
username, password, salt, email, type, status, activation_code, header_url, create_time
</ sql>
< sql id = " selectFields" >
id, username, password, salt, email, type, status, activation_code, header_url, create_time
</ sql>
< select id = " selectById" resultType = " User" >
select < include refid = " selectFields" > </ include>
from user
where id = #{id}
</ select>
< select id = " selectByName" resultType = " User" >
select < include refid = " selectFields" > </ include>
from user
where username = #{username}
</ select>
< select id = " selectByEmail" resultType = " User" >
select < include refid = " selectFields" > </ include>
from user
where email = #{email}
</ select>
< insert id = " insertUser" parameterType = " User" keyProperty = " id" >
insert into user (< include refid = " insertFields" > </ include> )
values(#{username}, #{password}, #{salt}, #{email}, #{type}, #{status}, #{activationCode}, #{headerUrl}, #{createTime})
</ insert>
< update id = " updateStatus" >
update user set status = #{status} where id = #{id}
</ update>
< update id = " updateHeader" >
update user set header_url = #{headerUrl} where id = #{id}
</ update>
< update id = " updatePassword" >
update user set password = #{password} where id = #{id}
</ update>
</ mapper>
Service层
@Service
public class UserService implements CommunityConstant {
@Autowired
private UserMapper userMapper;
@Autowired
private MailClient mailClient;
@Autowired
private TemplateEngine templateEngine;
@Value ( "${community.path.domain}" )
private String domain;
@Value ( "${server.servlet.context-path}" )
private String contextPath;
public User findUserById ( int id) {
return userMapper. selectById ( id) ;
}
public Map< String, Object> register ( User user) {
Map< String, Object> map = new HashMap < > ( ) ;
if ( user == null) {
throw new IllegalArgumentException ( "参数不能为空!" ) ;
}
if ( StringUtils. isBlank ( user. getUsername ( ) ) ) {
map. put ( "usernameMsg" , "账号不能为空!" ) ;
return map;
}
if ( StringUtils. isBlank ( user. getPassword ( ) ) ) {
map. put ( "passwordMsg" , "密码不能为空!" ) ;
return map;
}
if ( StringUtils. isBlank ( user. getEmail ( ) ) ) {
map. put ( "emailMsg" , "邮箱不能为空!" ) ;
return map;
}
User u = userMapper. selectByName ( user. getUsername ( ) ) ;
if ( u != null) {
map. put ( "usernameMsg" , "该账号已存在!" ) ;
return map;
}
u = userMapper. selectByEmail ( user. getEmail ( ) ) ;
if ( u != null) {
map. put ( "emailMsg" , "该邮箱已被注册!" ) ;
return map;
}
user. setSalt ( CommunityUtil. generateUUID ( ) . substring ( 0 , 5 ) ) ;
user. setPassword ( CommunityUtil. md5 ( user. getPassword ( ) + user. getSalt ( ) ) ) ;
user. setType ( 0 ) ;
user. setStatus ( 0 ) ;
user. setActivationCode ( CommunityUtil. generateUUID ( ) ) ;
user. setHeaderUrl ( String. format ( "http://images.nowcoder.com/head/%dt.png" , new Random ( ) . nextInt ( 1000 ) ) ) ;
user. setCreateTime ( new Date ( ) ) ;
userMapper. insertUser ( user) ;
Context context = new Context ( ) ;
context. setVariable ( "email" , user. getEmail ( ) ) ;
String url = domain + contextPath + "/activation/" + user. getId ( ) + "/" + user. getActivationCode ( ) ;
context. setVariable ( "url" , url) ;
String content = templateEngine. process ( "/mail/activation" , context) ;
mailClient. sendMail ( user. getEmail ( ) , "激活账号" , content) ;
return map;
}
public int activation ( int userId, String code) {
User user = userMapper. selectById ( userId) ;
if ( user. getStatus ( ) == 1 ) {
return ACTIVATION_REPEAT;
} else if ( user. getActivationCode ( ) . equals ( code) ) {
userMapper. updateStatus ( userId, 1 ) ;
return ACTIVATION_SUCCESS;
} else {
return ACTIVATION_FAILURE;
}
}
}
Controller层
@Controller
public class LoginController implements CommunityConstant {
@Autowired
private UserService userService;
@RequestMapping ( path = "/register" , method = RequestMethod. GET)
public String getRegisterPage ( ) {
return "/site/register" ;
}
@RequestMapping ( path = "/login" , method = RequestMethod. GET)
public String getLoginPage ( ) {
return "/site/login" ;
}
@RequestMapping ( path = "/register" , method = RequestMethod. POST)
public String register ( Model model, User user) {
Map< String, Object> map = userService. register ( user) ;
if ( map == null || map. isEmpty ( ) ) {
model. addAttribute ( "msg" , "注册成功,我们已经向您的邮箱发送了一封激活邮件,请尽快激活!" ) ;
model. addAttribute ( "target" , "/index" ) ;
return "/site/operate-result" ;
} else {
model. addAttribute ( "usernameMsg" , map. get ( "usernameMsg" ) ) ;
model. addAttribute ( "passwordMsg" , map. get ( "passwordMsg" ) ) ;
model. addAttribute ( "emailMsg" , map. get ( "emailMsg" ) ) ;
return "/site/register" ;
}
}
@RequestMapping ( path = "/activation/{userId}/{code}" , method = RequestMethod. GET)
public String activation ( Model model, @PathVariable ( "userId" ) int userId, @PathVariable ( "code" ) String code) {
int result = userService. activation ( userId, code) ;
if ( result == ACTIVATION_SUCCESS) {
model. addAttribute ( "msg" , "激活成功,您的账号已经可以正常使用了!" ) ;
model. addAttribute ( "target" , "/login" ) ;
} else if ( result == ACTIVATION_REPEAT) {
model. addAttribute ( "msg" , "无效操作,该账号已经激活过了!" ) ;
model. addAttribute ( "target" , "/index" ) ;
} else {
model. addAttribute ( "msg" , "激活失败,您提供的激活码不正确!" ) ;
model. addAttribute ( "target" , "/index" ) ;
}
return "/site/operate-result" ;
}
}
注册页面register.html
< div class = " main" >
< div class = " container pl-5 pr-5 pt-3 pb-3 mt-3 mb-3" >
< h3 class = " text-center text-info border-bottom pb-3" > 注 册</ h3>
< form class = " mt-5" method = " post" th: action= " @{/register}" >
< div class = " form-group row" >
< label for = " username" class = " col-sm-2 col-form-label text-right" > 账号:</ label>
< div class = " col-sm-10" >
< input type = " text"
th: class= " |form-control ${usernameMsg!=null?' is-invalid' :' ' }|"
th: value= " ${user!=null?user.username:' ' }"
id = " username" name = " username" placeholder = " 请输入您的账号!" required >
< div class = " invalid-feedback" th: text= " ${usernameMsg}" >
该账号已存在!
</ div>
</ div>
</ div>
< div class = " form-group row mt-4" >
< label for = " password" class = " col-sm-2 col-form-label text-right" > 密码:</ label>
< div class = " col-sm-10" >
< input type = " password"
th: class= " |form-control ${passwordMsg!=null?' is-invalid' :' ' }|"
th: value= " ${user!=null?user.password:' ' }"
id = " password" name = " password" placeholder = " 请输入您的密码!" required >
< div class = " invalid-feedback" th: text= " ${passwordMsg}" >
密码长度不能小于8位!
</ div>
</ div>
</ div>
< div class = " form-group row mt-4" >
< label for = " confirm-password" class = " col-sm-2 col-form-label text-right" > 确认密码:</ label>
< div class = " col-sm-10" >
< input type = " password" class = " form-control"
th: value= " ${user!=null?user.password:' ' }"
id = " confirm-password" placeholder = " 请再次输入密码!" required >
< div class = " invalid-feedback" >
两次输入的密码不一致!
</ div>
</ div>
</ div>
< div class = " form-group row" >
< label for = " email" class = " col-sm-2 col-form-label text-right" > 邮箱:</ label>
< div class = " col-sm-10" >
< input type = " email"
th: class= " |form-control ${emailMsg!=null?' is-invalid' :' ' }|"
th: value= " ${user!=null?user.email:' ' }"
id = " email" name = " email" placeholder = " 请输入您的邮箱!" required >
< div class = " invalid-feedback" th: text= " ${emailMsg}" >
该邮箱已注册!
</ div>
</ div>
</ div>
< div class = " form-group row mt-4" >
< div class = " col-sm-2" > </ div>
< div class = " col-sm-10 text-center" >
< button type = " submit" class = " btn btn-info text-white form-control" > 立即注册</ button>
</ div>
</ div>
</ form>
</ div>
</ div>
激活页面activation.html
< body>
< div>
< p>
< b th: text= " ${email}" > xxx@xxx.com</ b> , 您好!
</ p>
< p>
您正在注册, 这是一封激活邮件, 请点击
< a th: href= " ${url}" > 此链接</ a> ,
激活您的账号!
</ p>
</ div>
</ body>
注册成功页面operate-result.html
< div class = " main" >
< div class = " container mt-5" >
< div class = " jumbotron" >
< p class = " lead" th: text= " ${msg}" > 您的账号已经激活成功,可以正常使用了!</ p>
< hr class = " my-4" >
< p>
系统会在 < span id = " seconds" class = " text-danger" > 8</ span> 秒后自动跳转,
您也可以点此 < a id = " target" th: href= " @{${target}}" class = " text-primary" > 链接</ a> , 手动跳转!
</ p>
</ div>
</ div>
</ div>
< script>
$ ( function ( ) {
setInterval ( function ( ) {
var seconds = $ ( "#seconds" ) . text ( ) ;
$ ( "#seconds" ) . text ( -- seconds) ;
if ( seconds == 0 ) {
location. href = $ ( "#target" ) . attr ( "href" ) ;
}
} , 1000 ) ;
} ) ;
</ script>