一、项目结构
二、数据库设计
/*
Navicat MySQL Data Transfer
Source Server : localhost
Source Server Version : 50529
Source Host : localhost:3306
Source Database : security
Target Server Type : MYSQL
Target Server Version : 50529
File Encoding : 65001
Date: 2019-12-21 14:04:04
*/
SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for `authority_menu`
-- ----------------------------
DROP TABLE IF EXISTS `authority_menu`;
CREATE TABLE `authority_menu` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'id',
`url` varchar(255) DEFAULT NULL COMMENT '请求路径',
`menu_name` varchar(255) DEFAULT NULL COMMENT '菜单名称',
`parent_id` int(11) DEFAULT NULL COMMENT '父菜单id',
`update_time` varchar(255) DEFAULT NULL COMMENT '更新时间',
`remark` varchar(255) DEFAULT NULL COMMENT '备注',
`url_pre` varchar(255) DEFAULT NULL COMMENT '路由(前端自己匹配用)',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of authority_menu
-- ----------------------------
-- ----------------------------
-- Table structure for `authority_role`
-- ----------------------------
DROP TABLE IF EXISTS `authority_role`;
CREATE TABLE `authority_role` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'id',
`role_name` varchar(255) DEFAULT NULL COMMENT '角色名称(必须以ROLE_起始命名)',
`role_name_CN` varchar(255) DEFAULT NULL COMMENT '角色名称中文',
`update_time` varchar(255) DEFAULT NULL COMMENT '更新时间',
`remark` varchar(255) DEFAULT NULL COMMENT '备注',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=35 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of authority_role
-- ----------------------------
-- ----------------------------
-- Table structure for `authority_role_menu`
-- ----------------------------
DROP TABLE IF EXISTS `authority_role_menu`;
CREATE TABLE `authority_role_menu` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'id',
`role_id` int(11) DEFAULT NULL,
`menu_id` int(11) DEFAULT NULL,
`update_time` varchar(255) DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of authority_role_menu
-- ----------------------------
-- ----------------------------
-- Table structure for `authority_user`
-- ----------------------------
DROP TABLE IF EXISTS `authority_user`;
CREATE TABLE `authority_user` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'id',
`username` varchar(255) DEFAULT NULL COMMENT '用户名',
`password` varchar(255) DEFAULT NULL COMMENT '密码',
`email` varchar(255) DEFAULT NULL COMMENT '邮箱',
`phone` varchar(255) DEFAULT NULL COMMENT '手机号',
`valid_time` varchar(255) DEFAULT NULL COMMENT '有效截止时间',
`update_time` varchar(255) DEFAULT NULL COMMENT '更新时间',
`remark` mediumtext COMMENT '备注',
`nickname` varchar(255) DEFAULT NULL COMMENT '昵称',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=47 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of authority_user
-- ----------------------------
-- ----------------------------
-- Table structure for `authority_user_role`
-- ----------------------------
DROP TABLE IF EXISTS `authority_user_role`;
CREATE TABLE `authority_user_role` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'id',
`user_id` int(11) DEFAULT NULL,
`role_id` int(11) DEFAULT NULL,
`update_time` varchar(255) DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=74 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of authority_user_role
-- ----------------------------
-- ----------------------------
-- Table structure for `persistent_logins`
-- ----------------------------
DROP TABLE IF EXISTS `persistent_logins`;
CREATE TABLE `persistent_logins` (
`username` varchar(64) NOT NULL,
`series` varchar(64) NOT NULL,
`token` varchar(64) NOT NULL,
`last_used` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`series`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of persistent_logins
-- ----------------------------
三、controller
1、UserController
package cn.**.controller;
import cn.**.dao.UserDao;
import cn.**.service.UserService1;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @ProjectName: springbootSecurity
* @Package: cn.**.security.controller
* @Author: huat
* @Date: 2019/12/12 14:56
* @Version: 1.0
*/
@RestController
public class UserController {
@Autowired
UserService1 userService1;
//@Secured("ROLE_ADMIN")//security权限注解
//@RolesAllowed("ROLE_ADMIN") //jsr250注解
//@PreAuthorize("hasRole('ROLE_ADMIN')")//spring的注解
@RequestMapping("/login")
public String login(String username,String password){
//获取登陆的用户名
String username1= SecurityContextHolder.getContext().getAuthentication().getName();
System.out.println(username);
return "index";
}
@RequestMapping("test")
public String test(){
return "hello test";
}
}
2、IntoController
package cn.**.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* @ProjectName: springbootSecurity
* @Package: cn.**.security.controller
* @Author: huat
* @Date: 2019/12/16 15:16
* @Version: 1.0
*/
@Controller
public class IntoController {
@RequestMapping("intoTest")
public String intoTest(){
return "/test";
}
@RequestMapping("intoIndex")
public String intoIndex(){
return "index";
}
@RequestMapping("intoFail")
public String intoFail(){
return "fail";
}
@RequestMapping("intoLogin")
public String intoLogin(){
return "login";
}
@RequestMapping("dologin")
public String dologin(){
return "index";
}
}
四、entity
1、AuthorityUser
package cn.**.entity;
import com.fasterxml.jackson.annotation.JsonIgnore;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
import java.util.List;
/**
* @ProjectName: springbootSecurity
* @Package: cn.**.security.entity
* @Author: huat
* @Date: 2019/12/12 15:12
* @Version: 1.0
* 创建实体类第一种方式
*/
public class AuthorityUser implements UserDetails {
private int id;
private String username;
private String password;
private List<AuthorityRole> authorityRoles;
public int getId() {
return id;
}
public void setId(int 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 List<AuthorityRole> getAuthorityRoles() {
return authorityRoles;
}
public void setAuthorityRoles(List<AuthorityRole> authorityRoles) {
this.authorityRoles = authorityRoles;
}
@Override
@JsonIgnore//忽略此属性 转成json字符串时不进行转换
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorityRoles;
}
@Override
@JsonIgnore//忽略此属性 转成json字符串时不进行转换
public boolean isAccountNonExpired() {
return true;
}
@Override
@JsonIgnore//忽略此属性 转成json字符串时不进行转换
public boolean isAccountNonLocked() {
return true;
}
@Override
@JsonIgnore//忽略此属性 转成json字符串时不进行转换
public boolean isCredentialsNonExpired() {
return true;
}
@Override
@JsonIgnore//忽略此属性 转成json字符串时不进行转换
public boolean isEnabled() {
return true;
}
}
2、AuthorityRole
package cn.**.entity;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.annotation.JsonAppend;
import org.springframework.security.core.GrantedAuthority;
/**
* @ProjectName: springbootSecurity
* @Package: cn.**.security.entity
* @Author: huat
* @Date: 2019/12/12 16:14
* @Version: 1.0
*/
public class AuthorityRole implements GrantedAuthority {
private int rid;
private String roleName;
private String roleNameCN;
public int getRid() {
return rid;
}
public void setRid(int rid) {
this.rid = rid;
}
public String getRoleName() {
return roleName;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
public String getRoleNameCN() {
return roleNameCN;
}
public void setRoleNameCN(String roleNameCN) {
this.roleNameCN = roleNameCN;
}
@JsonIgnore//忽略此属性 转成json字符串时不进行转换
@Override
public String getAuthority() {
return roleName;
}
}
3、AuthorityMenu
package cn.**.entity;
/**
* @ProjectName: springbootSecurity
* @Package: cn.*.security.entity
* @Author: huat
* @Date: 2019/12/16 18:45
* @Version: 1.0
*/
public class AuthorityMenu {
private int id;
private String url;
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
五、service
1、UserService
package cn.**.service;
import org.springframework.security.core.userdetails.UserDetailsService;
/**
* @ProjectName: springbootSecurity
* @Package: cn.**.security.service
* @Author: huat
* @Date: 2019/12/12 17:35
* @Version: 1.0
*/
//UserDetailsService 是security中的类
public interface UserService extends UserDetailsService {
}
2、UserServiceImpl
package cn.**.service;
import cn.**.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
/**
* @ProjectName: springbootSecurity
* @Package: cn.**.utile
* @Author: huat
* @Date: 2019/12/12 14:48
* @Version: 1.0
*/
@Service//将这个类注入到spring容器中
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
return userDao.getUser(s);
}
public static void main(String[] args) {
BCryptPasswordEncoder passwordEncoder=new BCryptPasswordEncoder();
//passwordEncoder.encode("123456")加密方法
//执行多次加密后的结果均不一样
//执行第一次:$2a$10$RSlzzO4xAubBLZMB8jPL8eMFX17gM61JfrgINpzOiaEsBdtKBuSDe
//执行第二次:$2a$10$2uQWdP9qAXGK4H0um0gtG.o1QzbGhBlbxx8YjX526jIrVBNKyziFe
//执行第三次:$2a$10$8CWttAISfamloSErsqEAPemALyZvj8VWaUCBJKcm2GE1dhFR.7oYG
//security加密方式是动态加盐
System.out.println(passwordEncoder.encode("123456"));
//比较明文和密文是否一致
System.out.println(passwordEncoder.matches("123456","$2a$10$lstWyJ6NlbdZ2uVr8ncsHOUKsG0VAT4FzDdy0gqy6gbaOqpp6ouDq"));
}
}
六、dao
1、UserDao
package cn.**.dao;
import cn.**.entity.AuthorityUser;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
/**
* @ProjectName: springbootSecurity
* @Package: cn.**.security.dao
* @Author: huat
* @Date: 2019/12/12 15:04
* @Version: 1.0
*/
@Mapper
public interface UserDao {
/**
* 根据用户名查询角色
* @param username 用户名
* @return
*/
AuthorityUser getUser(@Param("username") String username);
/**
* 添加用户
* @param username 用户名
* @param password 密码
* @return
*/
int saveUser(@Param("username") String username, @Param("password") String password);
}
UserDao.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="cn.**.dao.UserDao">
<resultMap id="userRole" type="authorityUser">
<id column="id" property="id"></id>
<result column="username" property="username"></result>
<result column="password" property="password"></result>
<collection property="authorityRoles" ofType="authorityRole">
<id column="rid" property="rid"></id>
<result property="roleName" column="role_name"></result>
<result property="roleNameCN" column="role_name_CN"></result>
</collection>
</resultMap>
<select id="getUser" resultMap="userRole">
select u.*,r.id rid,r.role_name,r.role_name_CN from authority_user u
left join authority_user_role ur
on ur.user_id=u.id
left join authority_role r
on ur.role_id=r.id
where username=#{username}
</select>
<insert id="saveUser">
insert into authority_user(username,password) values(#{username},#{password})
</insert>
</mapper>
七、SpringSercurityConfig
package cn.**.util;
import cn.**.service.UserService;
import cn.**.service.UserService1;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
/**
* @ProjectName: springbootSecurity
* @Package: cn.**.security.util
* @Author: huat
* @Date: 2019/12/14 8:06
* @Version: 1.0
*/
/**
* 开启security注解支持
* @EnableWebSecurity
* (securedEnabled=true) 开启@Secured 注解过滤权限
* (jsr250Enabled=true)开启@RolesAllowed 注解过滤权限
* (prePostEnabled=true) 使用表达式时间方法级别的安全性 4个注解可用
* @EnableGlobalMethodSecurity(prePostEnabled = true,securedEnabled=true,jsr250Enabled=true)
*/
@Configuration
@EnableWebSecurity
//@EnableGlobalMethodSecurity(prePostEnabled = true)
@EnableGlobalMethodSecurity(prePostEnabled = true,securedEnabled=true,jsr250Enabled=true)
public class SpringSercurityConfig extends WebSecurityConfigurerAdapter {
/* @Autowired
UserService1 userService;*/
@Autowired
UserService userService;
@Autowired
AuthenticationSuccessHandler authenticationSuccessHandler;//ajax登陆成功使用
@Autowired
AuthenticationFailureHandler authenticationFailureHandler;//ajax登陆失败使用
/**
* 将security中加密方式注入到spring容器中
* @return
*/
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
/**
* 将账号密码设置在数据库当中
* @param auth
* @throws Exception
*/
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
//将UserDetailsService放到容器中
.userDetailsService(userService)
//加密方式放入
.passwordEncoder(passwordEncoder());
}
@Override
public void configure(HttpSecurity http) throws Exception {
//释放静态资源,指定资源拦截规则,
// 指定自定义认证页面,指定退出认证配置,csrf(跨域伪造请求)配置
http.authorizeRequests()
.antMatchers("intoLogin","login.jsp","/css/**","fail.jsp","/intoIndex","/index.jsp").permitAll()//释放这些资源,允许匿名访问
.antMatchers("/**").hasAnyRole("ADMIN","USER")
.anyRequest().authenticated()//其他资源需要认证
.and()
.formLogin()
.loginPage("/intoLogin")//登陆页请求的接口
.loginProcessingUrl("/dologin")//登陆地址,由springSecurity提供
.successForwardUrl("/intoTest")//登陆成功
.failureForwardUrl("/intoFail")//登录失败
.permitAll()//指定所有资源释放
.and()
.logout()//登出
.logoutUrl("/logout")//指定登出路径
.logoutSuccessUrl("/login.jsp")//登出成功后跳转的url
.invalidateHttpSession(true)//是否清空session
.permitAll()
.and()
.csrf()
.disable();//关闭csrf(跨域伪造请求)
}
/* *//**
* ajax 登陆
* @param http
* @throws Exception
*//*
@Override
public void configure(HttpSecurity http) throws Exception {
//释放静态资源,指定资源拦截规则,
// 指定自定义认证页面,指定退出认证配置,csrf(跨域伪造请求)配置
http.authorizeRequests()
.antMatchers("intoLogin","login.jsp","/css/**","fail.jsp","/intoIndex","/index.jsp").permitAll()//释放这些资源,允许匿名访问
.antMatchers("/**").hasAnyRole("ADMIN","USER")
.antMatchers("/test").hasRole("USER")
.anyRequest().authenticated()//其他资源需要认证
.and()
.formLogin()
.loginPage("/intoLogin")//登陆页请求的接口
.successHandler(authenticationSuccessHandler)//登陆成功后返回的数据
.failureHandler(authenticationFailureHandler)
.loginProcessingUrl("/login")//登陆地址,由springSecurity提供
.usernameParameter("name")
.passwordParameter("pwd")
.permitAll()//指定所有资源释放
.and()
.logout()//登出
.logoutUrl("/logout")//指定登出路径
.logoutSuccessUrl("/login.jsp")//登出成功后跳转的url
.invalidateHttpSession(true)//是否清空session
.permitAll()
.and()
.cors()
.and()
.csrf()
.disable()
;//关闭csrf(跨域伪造请求)
}*/
}
八、异常处理类
package cn.**.controller;
import javax.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* @ProjectName: springSecurity
* @Package: cn.**.controller
* @Author: huat
* @Date: 2019/12/23 9:43
* @Version: 1.0
*
* 处理异常页面
*/
@Controller
public class MyException implements ErrorController {
private static Logger log = LoggerFactory.getLogger(MyException.class);
@RequestMapping("/error")
public String handleError(HttpServletRequest request) {
log.info("进入异常跳转");
Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");
switch (statusCode) {
case 404:
log.info("404异常跳转");
return "fail";
case 403:
log.info("403异常跳转");
return "403";
case 500:
log.info("500异常跳转");
return "/error/500";
default:
log.info("默认异常跳转");
return "/error/404";
}
}
@Override
public String getErrorPath() {
return "/error";
}
}
九、定义登陆成功和失败返回
package cn.**.util;
import com.alibaba.fastjson.JSON;
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;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;
/**
* @ProjectName: securityAjax
* @Package: cn.**.util
* @Author: huat
* @Date: 2019/12/26 9:50
* @Version: 1.0
* 登陆成功返回
*/
@Component
public class MyAuthenticationFailureHandler implements AuthenticationFailureHandler {
@Override
public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
Map<String,Object> map=new HashMap<String,Object>();
map.put("code",500);
map.put("msg","登陆失败");
map.put("data","");
/* httpServletResponse.setContentType("application/json;charset=utf-8");*/
PrintWriter out = httpServletResponse.getWriter();
out.write(JSON.toJSONString(map));
out.flush();
out.close();
}
}
package cn.**.util;
import com.alibaba.fastjson.JSON;
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;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;
/**
* @ProjectName: securityAjax
* @Package: cn.**.util
* @Author: huat
* @Date: 2019/12/26 9:41
* @Version: 1.0
*/
@Component
public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
System.out.println(httpServletRequest.getParameter("name"));
Map<String,Object> map=new HashMap<String,Object>();
map.put("code",200);
map.put("msg","登陆成功");
map.put("data","");
/* httpServletResponse.setContentType("application/json;charset=utf-8");*/
PrintWriter out = httpServletResponse.getWriter();
out.write(JSON.toJSONString(map));
out.flush();
out.close();
}
}