SpringSecurity(前后端不分离)

SpringSecurity Oauth2(前后端不分离)

简介

Spring Security 是 Spring 家族中的一个安全管理框架。相比与另外一个安全框架Shiro,它提供了更丰富的功能,社区资源也比Shiro丰富。

​ 一般来说中大型的项目都是使用SpringSecurity 来做安全框架。小项目有Shiro的比较多,因为相比与SpringSecurity,Shiro的上手更加的简单。

​ 一般Web应用的需要进行认证授权

认证:验证当前访问系统的是不是本系统的用户,并且要确认具体是哪个用户

授权:经过认证后判断当前用户是否有权限进行某个操作

​ 而认证和授权也是SpringSecurity作为安全框架的核心功能。

认证模式

实现Basic认证

Basic认证是一种较为简单的HTTP认证方式,客户端通过明文(Base64编码格式)户名和密码到服务器进行认证,通过常需要配合HTTPS来保证传输的安全

@***ponent
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    /*
    /新增Security账户 授权的账户
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //需要经过wzx
        auth.inMemoryAuthentication().withUser("wzx").password("456").authorities("/");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //配置认证方式 1.token 2.form表单
        http.authorizeHttpRequests().antMatchers("/**").fullyAuthenticated().and().httpBasic();
    }

}

form 表单模式

From 表单模式 适合于传统模式项目 前端和后端都是我们Java开发人员自己实现。Vue+SpringBoot

@***ponent
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    /*
    /新增Security账户 授权的账户
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //需要经过wzx
        auth.inMemoryAuthentication().withUser("admin").password("admin").authorities("/");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //配置认证方式 1.token 2.form表单 设置为BasicHttp认证
        http.authorizeHttpRequests().antMatchers("/**").authenticated().and().formLogin();
    }

    @Bean
    public static NoOpPasswordEncoder passwordEncoder(){
        return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance();
    }

}

配置权限策略

在企业管理系统平台中,会拆分成n多个不同的账号,每个账号对应不同的接口访问权限,

比如

账号admin所有接口都可以访问;

其他账号只能根据自己的权限进行访问;

//静态配置
@***ponent
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    /*
    /新增Security账户 授权的账户
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //需要经过wzx
        auth.inMemoryAuthentication().withUser("admin").password("admin").authorities("add");
        auth.inMemoryAuthentication().withUser("wzx").password("wzx").authorities("update");
        //对当前账户进行授权
    }


    //拦截
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //配置认证方式 1.token 2.form表单
         //http.authorizeHttpRequests().antMatchers("/**").authenticated().and().formLogin();
        //.antMatchers("add")方法中设置接口名称 .hasAnyAuthority("add")方法中防止接口所对应的权限
        http.authorizeHttpRequests()
                .antMatchers("/user/add").hasAnyAuthority("add")
                .antMatchers("/user/delete").hasAnyAuthority("delete")
                .antMatchers("/user/show").hasAnyAuthority("show")
                .antMatchers("/user/update").hasAnyAuthority("update")
                .antMatchers("/**").authenticated().and().formLogin();
    }

    @Bean
    public static NoOpPasswordEncoder passwordEncoder(){
        return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance();
    }

权限不足页面

统一返回异常类

/*
* 统一返回错误异常类
* */
@RestController
public class ErrorController {

    @RequestMapping("/error/403")
    public String error(){
        return "权限不足";
    }
}   
@Configuration
public class WebServerAutoConfig {

    @Bean
    public ConfigurableServletWebServerFactory webServerFactory() {
        TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
        ErrorPage errorPage400 = new ErrorPage(HttpStatus.BAD_REQUEST, "/error/400");
        ErrorPage errorPage401 = new ErrorPage(HttpStatus.UNAUTHORIZED, "/error/401");
        ErrorPage errorPage403 = new ErrorPage(HttpStatus.FORBIDDEN, "/error/403");
        ErrorPage errorPage404 = new ErrorPage(HttpStatus.NOT_FOUND, "/error/404");
        ErrorPage errorPage415 = new ErrorPage(HttpStatus.UNSUPPORTED_MEDIA_TYPE, "/error/415");
        ErrorPage errorPage500 = new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/error/500");
        factory.addErrorPages(errorPage400, errorPage401, errorPage403, errorPage404, errorPage415, errorPage500);
        return factory;
    }
}

自定义登录界面

   //拦截
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //配置认证方式 1.token 2.form表单
         //http.authorizeHttpRequests().antMatchers("/**").authenticated().and().formLogin();
        //.antMatchers("add")方法中设置接口名称 .hasAnyAuthority("add")方法中防止接口所对应的权限
        http.authorizeHttpRequests()
                .antMatchers("/user/add").hasAnyAuthority("add")
                .antMatchers("/user/delete").hasAnyAuthority("delete")
                .antMatchers("/user/show").hasAnyAuthority("show")
                .antMatchers("/user/update").hasAnyAuthority("update")
                //可以允许login 登录界面不被拦截
                .antMatchers("/login.html").permitAll()
                //设置自定义登录页面
                .antMatchers("/**").authenticated().and().formLogin()
                .loginPage("/login.html") //自定义登录界面
                .loginProcessingUrl("/login") // 登录接口,与form表单提交链接对应
//                .defaultSu***essUrl("/index.html") //登录成功跳转的界面
                .and().csrf().disable();
    } 
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="/login" method="post">
    <span>用户名称</span><input type="text" name="username"/> <br>
    <span>用户密码</span><input type="password" name="password"/> <br>
    <input type="submit" value="login">
</form>
</body>
</html>

动态整合RABC权限架构设计

它就是用db查询直接去数据库区查询所具备的所有权限规则和对用户进行授权

1.实现动态拦截

上面是所谓的静态权限拦截,那么动态拦截就是用db查询去数据库查询所有的权限和规则

//拦截
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //配置认证方式 1.token 2.form表单       
        //动态拦截
        ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry
                authorizeHttpRequests = http.authorizeRequests();
        //查询数据库中所有的权限
        List<Perm> permList = permMapper.queryAllPerm();   
        permList.forEach( p -> {
            //动态将接口和所对应的规则匹配
            authorizeHttpRequests.antMatchers(p.permUrl).hasAnyAuthority(p.permDesc);
        });
        authorizeHttpRequests
                //可以允许login 登录界面不被拦截
                .antMatchers("/login.html").permitAll()
                .antMatchers("/test.do").permitAll()
                //设置自定义登录页面
                .antMatchers("/**").authenticated().and().formLogin()
                .loginPage("/login.html") //自定义登录界面
                .loginProcessingUrl("/login") // 登录接口,与form表单提交链接对应
                .defaultSu***essUrl("/index.html") //登录成功跳转的界面
                .and().csrf().disable();
    } 

2.实现动态授权

首先实现动态授权要穿件一个MemberDetilsService实现UserDetilsService接口,其中loadUserByUsername() 方法是登录后加载该用户所具备的权限,用户实体类也必须要实现UserDetils 接口。

@Service
public class MembersDetailsService implements UserDetailsService {

    @Autowired
    UserMapper userMapper;

    @Autowired
    PermMapper permMapper;

    //登录加载user
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //1.在登录的时候 调用该方法 userName查询账户是否存在 在验证账户的密码
        User user = userMapper.queryByUserName(username);
        if (user == null) {
            return null;
        }
        //2.再根据账户的userID 关联查询 角色对应的权限 动态进行添加授权
        List<Perm> userPermList = permMapper.queryByUserName(username);
        //设置权限
        ArrayList<GrantedAuthority> grantedAuthorities = new ArrayList<>();
        userPermList.forEach( item -> {
            grantedAuthorities.add(new SimpleGrantedAuthority(item.permDesc));
        });
        user.setAuthorities(grantedAuthorities);
        return user;
    }

}

用户实体类

@Data
@AllArgsConstructor
@NoArgsConstructor
//必须要实现UserDetails接口
public class User implements UserDetails {
    private Integer id; //用户id
    private String username;    //用户账号
    private String password;    //用户密码
    private List<Role> role;

    //用户所有的权限
    private List<GrantedAuthority> authorities = new ArrayList<>();

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return authorities;
    }

    @Override
    public boolean isA***ountNonExpired() {
        return true;
    }

    @Override
    public boolean isA***ountNonLocked() {
        return true;
    }

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

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


}

然后去配置动态授权,其中要注意用加密工具对密码实现加解密

@Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //对当前账户进行授权
        auth.userDetailsService(membersDetailsService).passwordEncoder(new PasswordEncoder() {
            @Override
            public String encode(CharSequence rawPassword) {
                //使用Md5加密算法
                return MD5Util.convertMD5((String) rawPassword);
            }

            @Override
            public boolean matches(CharSequence rawPassword, String encodedPassword) {
                //使用MD5加密后 与数据库中的密码MD5 后验证
                String rawPass = MD5Util.convertMD5((String) rawPassword);
                boolean result = rawPass.equals(MD5Util.convertMD5(encodedPassword));
                return result;
            }
        });
    }

MD5加密工具

public class MD5Util {

    /**
     * 使用 MD5算法加密生成32位md5码
     * @param str
     * @return
     */
    public static String string2MD5(String str) {
        byte[] secretBytes = null;
        try {
            secretBytes = MessageDigest.getInstance("md5").digest(
                    str.getBytes());
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("没有md5这个算法!");
        }
        String md5code = new BigInteger(1, secretBytes).toString(16);// 16进制数字
        // 如果生成数字未满32位,需要前面补0
        for (int i = 0; i < 32 - md5code.length(); i++) {
            md5code = "0" + md5code;
        }
        return md5code;
    }

    /**
     *     可逆的加密解密算法,执行一次加密,两次解密
     * @param str
     * @return
     */
    public static String convertMD5(String str){

        char[] a = str.toCharArray();
        for (int i = 0; i < a.length; i++){
            a[i] = (char) (a[i] ^ 't');
        }
        String s = new String(a);
        return s;

    }

}

整合oauth2

什么是oauth2?

OAuth2.0是一个授权协议,他允许软件应用代表(而不是充当)资源拥有者去访问资源拥有者的资源。应用向资源拥有者请求授权,然后去的令牌(Token),并用他来访问资源,并且资源拥有者不用向应用提供用户名和密码等敏感数据。

OAUTH角色划分

  1. Resource Server:被授权访问的资源

  2. Authotization Server: OAUTH2认证授权中心

  3. Recource Owner:用户

  4. Client: 使用API的合作伙伴

Oauth应用场景

  1. 第三方联合登录 如QQ、微信

  2. 开放接口 蚂蚁金服、腾讯开放接口

转载请说明出处内容投诉
CSS教程_站长资源网 » SpringSecurity(前后端不分离)

发表评论

欢迎 访客 发表评论

一个令你着迷的主题!

查看演示 官网购买