Spring Security


SpringSecurity项目创建

  • 引入security依赖,项目中所有的接口都被保护起来了
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-security</artifactId>
		</dependency>
  • 启动项目浏览器访问都会跳转到下图所示的页面
    在这里插入图片描述
  • 登录的用户名和密码
    用户名:user
    密码:
    在这里插入图片描述

手工配置用户名密码

由于每次的密码是自动生成的,可以手工配置用户名密码。

数据库配置

配置文件配置

用户名和密码是写死的

application.yml

spring:
  security:
    user:
      password: 123
      name: user
      roles:
        - admin

Java代码配置

protected void configure(AuthenticationManagerBuilder auth){}

HttpSecurity 配置不同的权限控制方法

配置不同的拦截规则,对不同的url采取不同的策略

protected void configure(HttpSecurity http){}

登录表单详细配置

登录成功

successHandler(new AuthenticationSuccessHandler() {})

登录失败

failureHandler(new AuthenticationFailureHandler() {})

登录异常类:AuthenticationException.java
在这里插入图片描述

注销登录

logoutSuccessHandler(new LogoutSuccessHandler(){})

多个 HttpSecurity

MultiHttpSecurityConfig.java

密码加密BCryptPasswordEncoder

相同的明文,密文是不一样的

    @Bean
    PasswordEncoder passwordEncoder(){
        
        return new BCryptPasswordEncoder();
    }

方法安全

直接在方法上加注解,确保方法的安全。
1、@EnableGlobalMethodSecurity(prePostEnabled = true,securedEnabled = true)
2、HelloController.java
3、MethodService.java

基于数据库的认证

1、建表,user和user_role
2、创建bean,User和Role,User实现UserDetails接口
4、创建SecurityConfig.java配置类
3、创建service和controller,UserService实现UserDetailsService接口,UserController.java测试

角色继承

  • SpringBoot2.0.8(含)之前的写法
@Bean
RoleHierarchy roleHierarchy() {
    RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl();
    String hierarchy = "ROLE_dba > ROLE_admin ROLE_admin > ROLE_user";
    roleHierarchy.setHierarchy(hierarchy);
    return roleHierarchy;
}
  • SpringBoot2.0.8(含)之后的写法
@Bean
RoleHierarchy roleHierarchy() {
    RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl();
    String hierarchy = "ROLE_dba > ROLE_admin \n ROLE_admin > ROLE_user";
    roleHierarchy.setHierarchy(hierarchy);
    return roleHierarchy;
}

动态配置权限

在数据库里去实现权限的控制
1、创建五张表:user、role、user_role、menu、menu_role
2、配置security登录(见基于数据库的认证)
3、创建UserService.java,MenuService.java
4、创建UserMapper.java,MenuMapper.java
5、创建配置类:Myfilter实现FilterInvocationSecurityMetadataSource接口
6、创建配置类:MyAccessDecisionManager实现AccessDecisionManager接口
7、创建配置类:SercurityDynamicConfig继承WebSecurityConfigurerAdapter类
8、创建HelloController.java
代码

Spring Security 结合 OAuth2

代码

获取token
在这里插入图片描述
访问资源
在这里插入图片描述
获取新token
在这里插入图片描述

Spring Security 使用 JSON 登录

代码
在这里插入图片描述

Spring Security 整合 Jwt

代码
依赖:

		<dependency>
			<groupId>io.jsonwebtoken</groupId>
			<artifactId>jjwt</artifactId>
			<version>0.9.1</version>
		</dependency>

在这里插入图片描述

代码

SecurityConfig.class

手工配置用户名密码、登录成功、登录失败、注销登录

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.*;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;

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;

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    // 密码加密的编码器
    @Bean
    PasswordEncoder passwordEncoder(){
        // 告诉系统用户密码不加密
        return NoOpPasswordEncoder.getInstance();
    }

    /**
     * 手工配置用户名密码
     * @param auth
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                .withUser("user1").password("123").roles("admin")
                .and()
                .withUser("user2").password("456").roles("user");
    }

    // 拦截规则

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 开启配置
        http.authorizeRequests()
                // ant风格路径匹配,以admin开始的
                // hasRole具有一个角色,hasAnyRole具备多个角色中的任意一个
                .antMatchers("/admin/**").hasRole("admin")
                .antMatchers("user/**").hasAnyRole("admin","user")
//                .antMatchers("/user/**").access("hasAnyRole('admin','user')")
                // 剩下的其他请求都是登陆之后就能访问
                .anyRequest().authenticated()
                .and()
                // 表单登录
                // http://localhost:8080/dologin?uname=user1&pwd=123
                .formLogin()
                // 处理登录的url
                .loginProcessingUrl("/dologin")
                // 默认看到的登录页面
                .loginPage("/login")
                .usernameParameter("uname")
                .passwordParameter("pwd")
//登录成功  //////////////////////////////////////////////////
                // 前后端分离,只需要知道登录成功或者失败,返回Json
                // 登录成功处理器
                .successHandler(new AuthenticationSuccessHandler() {
                    // authentication里面保存了登录成功的信息
                    @Override
                    public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
                        httpServletResponse.setContentType("application/json;charset=UTF-8");
                        PrintWriter writer = httpServletResponse.getWriter();
                        HashMap<String, Object> map = new HashMap<>();
                        map.put("status",200);
                        // 得到登录成功的用户对象
                        map.put("msg",authentication.getPrincipal());
                        writer.write(new ObjectMapper().writeValueAsString(map));
                        writer.flush();
                        writer.close();
                    }
                })
                // 前后端不分,直接跳转到某一个页面
//                .successForwardUrl()
//登录成功  //////////////////////////////////////////////////

//登录失败  //////////////////////////////////////////////////
                // 登录失败
                .failureHandler(new AuthenticationFailureHandler() {
                    // AuthenticationException锁定登录失败的原因
                    @Override
                    public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
                        httpServletResponse.setContentType("application/json;charset=UTF-8");
                        PrintWriter writer = httpServletResponse.getWriter();
                        HashMap<String, Object> map = new HashMap<>();
                        map.put("status",401);
                        if(e instanceof LockedException){
                            map.put("msg","账户被锁定");
                        }else if(e instanceof BadCredentialsException){
                            // 错误凭证
                            map.put("msg","用户名或密码错误");
                        }else if(e instanceof DisabledException){
                            // 错误凭证
                            map.put("msg","账户被禁言");
                        }else if(e instanceof AccountExpiredException){
                            // 错误凭证
                            map.put("msg","账户过期");
                        }else if(e instanceof CredentialsExpiredException){
                            // 错误凭证
                            map.put("msg","密码过期");
                        }else{
                            // 错误凭证
                            map.put("msg","登录失败");
                        }
                        writer.write(new ObjectMapper().writeValueAsString(map));
                        writer.flush();
                        writer.close();
                    }
                })
//登录失败  //////////////////////////////////////////////////
                // 与登录有关的接口直接就能访问
                .permitAll()
                .and()
                .logout()
                .logoutUrl("/logout")
//注销登录  //////////////////////////////////////////////////
                // 注销成功的页面回调
                .logoutSuccessHandler(new LogoutSuccessHandler() {
                    @Override
                    public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
                        httpServletResponse.setContentType("application/json;charset=UTF-8");
                        PrintWriter writer = httpServletResponse.getWriter();
                        HashMap<String, Object> map = new HashMap<>();
                        map.put("status",200);
                        // 得到登录成功的用户对象
                        map.put("msg","注销登录成功");
                        writer.write(new ObjectMapper().writeValueAsString(map));
                        writer.flush();
                        writer.close();
                    }
                })
//注销登录  //////////////////////////////////////////////////
                .and()
                // 要用postman测试,SpringSecurity自带防止csrf攻击的策略
                .csrf().disable();
    }
}

MultiHttpSecurityConfig.java

多个 HttpSecurity

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
public class MultiHttpSecurityConfig {
    @Bean
    PasswordEncoder passwordEncoder(){
        return NoOpPasswordEncoder.getInstance();
    }

    @Autowired
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                .withUser("user1").password("111").roles("admin")
                .and()
                .withUser("user2").password("222").roles("user");
    }

    @Configuration
    // 设置优先级,数字越小越优先
    @Order(1)
    public static class AdminSercurityConfig extends WebSecurityConfigurerAdapter{
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.antMatcher("/admin/**").authorizeRequests().anyRequest().hasAnyRole("admin");
        }
    }

    @Configuration
    public static class OtherSercurityConfig extends WebSecurityConfigurerAdapter{
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.authorizeRequests().anyRequest().authenticated()
                    .and()
                    .formLogin()
                    .loginProcessingUrl("/dologin")
                    .permitAll()
                    .and()
                    .csrf().disable();
        }
    }
}

HelloController.java

import com.ll.service.MethodService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {
    @Autowired
    MethodService methodService;

    @GetMapping("/hello1")
    public  String hello1(){
        return methodService.hello();
    }

    @GetMapping("/hello2")
    public  String hello2(){
        return methodService.admin();
    }


    @GetMapping("/hello3")
    public  String hello3(){
        return methodService.user();
    }
}

MethodService.java

import org.springframework.security.access.annotation.Secured;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service;

@Service
public class MethodService {
    // 这个方法只有admin角色的用户才能执行
    @PreAuthorize("hasRole('admin')")
    public String admin(){
        return "这是admin能执行的方法";
    }

    @Secured("ROLE_user")
    public String user(){
        return "这是user能执行的方法";
    }

    @PreAuthorize("hasAnyRole('admin','user')")
    public String hello(){
        return "这是user和admin都能执行的方法";
    }
}

MultiHttpSecurityConfig.java

方法安全

@Configuration
// 开启方法安全
@EnableGlobalMethodSecurity(prePostEnabled = true,securedEnabled = true)
public class MultiHttpSecurityConfig {}

security.sql

/*
Navicat MySQL Data Transfer
Source Server         : localhost
Source Server Version : 50717
Source Host           : localhost:3306
Source Database       : security
Target Server Type    : MYSQL
Target Server Version : 50717
File Encoding         : 65001
Date: 2018-07-28 15:26:51
*/

SET FOREIGN_KEY_CHECKS=0;

-- ----------------------------
-- Table structure for role
-- ----------------------------
DROP TABLE IF EXISTS `role`;
CREATE TABLE `role` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(32) DEFAULT NULL,
  `nameZh` varchar(32) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of role
-- ----------------------------
INSERT INTO `role` VALUES ('1', 'dba', '数据库管理员');
INSERT INTO `role` VALUES ('2', 'admin', '系统管理员');
INSERT INTO `role` VALUES ('3', 'user', '用户');

-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(32) DEFAULT NULL,
  `password` varchar(255) DEFAULT NULL,
  `enabled` tinyint(1) DEFAULT NULL,
  `locked` tinyint(1) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES ('1', 'root', '$2a$10$RMuFXGQ5AtH4wOvkUqyvuecpqUSeoxZYqilXzbz50dceRsga.WYiq', '1', '0');
INSERT INTO `user` VALUES ('2', 'admin', '$2a$10$RMuFXGQ5AtH4wOvkUqyvuecpqUSeoxZYqilXzbz50dceRsga.WYiq', '1', '0');
INSERT INTO `user` VALUES ('3', 'sang', '$2a$10$RMuFXGQ5AtH4wOvkUqyvuecpqUSeoxZYqilXzbz50dceRsga.WYiq', '1', '0');

-- ----------------------------
-- Table structure for user_role
-- ----------------------------
DROP TABLE IF EXISTS `user_role`;
CREATE TABLE `user_role` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `uid` int(11) DEFAULT NULL,
  `rid` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of user_role
-- ----------------------------
INSERT INTO `user_role` VALUES ('1', '1', '1');
INSERT INTO `user_role` VALUES ('2', '1', '2');
INSERT INTO `user_role` VALUES ('3', '2', '2');
INSERT INTO `user_role` VALUES ('4', '3', '3');
SET FOREIGN_KEY_CHECKS=1;

UserDetails.java

public interface UserDetails extends Serializable {
	// 返回用户所有角色
    Collection<? extends GrantedAuthority> getAuthorities();

    String getPassword();

    String getUsername();

	// 账户是否未过期
    boolean isAccountNonExpired();

	// 账户是否未锁定
    boolean isAccountNonLocked();

	// 密码是否未过期
    boolean isCredentialsNonExpired();

    boolean isEnabled();
}

User.java

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

// 如果需要从数据库中加载用户,就需要实现
public class User implements UserDetails{
    private Integer id;
    private String username;
    private String password;
    private Boolean enabled;
    private Boolean locked;
    private List<Role> roles;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public Boolean getEnabled() {
        return enabled;
    }

    public void setEnabled(Boolean enabled) {
        this.enabled = enabled;
    }

    public List<Role> getRoles() {
        return roles;
    }

    public void setRoles(List<Role> roles) {
        this.roles = roles;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public void setLocked(Boolean locked) {
        this.locked = locked;
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        List<SimpleGrantedAuthority> authorities=new ArrayList<>();
        for (Role role:roles){
            // 角色必须以ROLE_为开头
            authorities.add(new SimpleGrantedAuthority("ROLE_"+role.getName()));
        }
        return authorities;
    }

    @Override
    public String getPassword() {
        return password;
    }

    @Override
    public String getUsername() {
        return username;
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return !locked;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return enabled;
    }
}

Role.java

public class Role {
    private Integer id;
    private String name;
    private String nameZh;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getNameZh() {
        return nameZh;
    }

    public void setNameZh(String nameZh) {
        this.nameZh = nameZh;
    }

    @Override
    public String toString() {
        return "Role{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", nameZh='" + nameZh + '\'' +
                '}';
    }
}

SecurityConfig.java

import com.ll.service.UserService;
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.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    UserService userService;

    @Bean
    PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userService);
    }
    
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/dba/**").hasRole("dba")
                .antMatchers("/admin/**").hasRole("admin")
                .antMatchers("/user/**").hasRole("user")
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .permitAll()
                .and()
                .csrf().disable();
    }
}

UserService.java

import com.ll.bean.User;
import com.ll.dao.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
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;

@Service
public class UserService implements UserDetailsService {
    @Autowired
    UserMapper userMapper;

    // 根据用户名去查询用户
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user=userMapper.loadUserByUsername(username);
        if(null==user){
            throw new UsernameNotFoundException("用户不存在");
        }
        user.setRoles(userMapper.getUserRolesById(user.getId()));
        return user;
    }
}

UserController.java

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class UserController {
    @GetMapping("/hello")
    public String hello(){
        return "hello!";
    }
    @GetMapping("/dba/hello")
    public String hello1(){
        return "hello dba!";
    }
    @GetMapping("/admin/hello")
    public String hello2(){
        return "hello admin!";
    }
    @GetMapping("/user/hello")
    public String hello3(){
        return "hello user!";
    }
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值