一、项目结构


配置spring security后返回体没有错误信息 spring security permitall_后端



二、数据库设计


配置spring security后返回体没有错误信息 spring security permitall_spring boot_02

/*
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();

    }
}