springboot 集成 security

springboot技术栈

  1. pxm.xml 引用开发包
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
  1. 创建User和Auth实体类 user实现UserDetails接口 Auth实现GrantedAuthority接口
package com.cloud.consumer.config.security.model;

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

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

/**
 * 开发公司:刘志强
 * 版权:刘志强
 * <p>
 * User
 *
 * @author 刘志强
 * @created Create Time: 2019/4/15
 */

public class User implements UserDetails {
    private String userName;
    private String password;

    private List<Auth> list;

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

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

    public List<Auth> getList() {
        return list;
    }

    public void setList(List<Auth> list) {
        this.list = list;
    }

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

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

    @Override
    public String getUsername() {
        return this.getUserName();
    }

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

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

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

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


package com.cloud.consumer.config.security.model;

import lombok.Data;
import org.springframework.security.core.GrantedAuthority;

/**
 * 开发公司:刘志强
 * 版权:刘志强
 * <p>
 * Permission
 *权限
 * @author 刘志强
 * @created Create Time: 2019/4/15
 */
@Data
public class Auth implements GrantedAuthority {

    public Auth(String authority){
        this.authority = authority;
    }
    private String authority;

    @Override
    public String getAuthority() {
        return this.authority;
    }
}
  1. 创建 SecurityConfig 配置文件
package com.cloud.consumer.config.security;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
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.NoOpPasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

/**
 * @author 刘志强
 * @date 2020/7/21 09:34
 * * @EnableGlobalMethodSecurity(securedEnabled=true)
 * * 开启@Secured 注解过滤权限
 * *
 * * @EnableGlobalMethodSecurity(jsr250Enabled=true)
 * * 开启@RolesAllowed 注解过滤权限
 * *
 * * @EnableGlobalMethodSecurity(prePostEnabled=true)
 * * 使用表达式时间方法级别的安全性 4个注解可用
 * * -@PreAuthorize 在方法调用之前,基于表达式的计算结果来限制对方法的访问
 * * -@PostAuthorize 允许方法调用,但是如果表达式计算结果为false,将抛出一个安全性异常
 * * -@PostFilter 允许方法调用,但必须按照表达式来过滤方法的结果
 * * -@PreFilter 允许方法调用,但必须在进入方法之前过滤输入值
 */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    private final UserDetailsServiceImpl userDetailsService;
    private final AuthorizationTokenFilter authenticationTokenFilter;

    public SecurityConfig(AuthorizationTokenFilter authenticationTokenFilter, UserDetailsServiceImpl userDetailsService) {
        this.authenticationTokenFilter = authenticationTokenFilter;
        this.userDetailsService = userDetailsService;
    }

    @Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        // 两种注册用户的方式
        auth.inMemoryAuthentication()
                .passwordEncoder(new BCryptPasswordEncoder())
                .withUser("admin")
                .password(new BCryptPasswordEncoder().encode("123456"))
                .roles("ROOT");
        auth.userDetailsService(userDetailsService).passwordEncoder(NoOpPasswordEncoder.getInstance());
    }

    @Override
    public void configure(HttpSecurity http) throws Exception {
        // 添加过滤器
        http.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);

        http.authorizeRequests()
                // /actuator 下的请求需要ROOT角色并开启 httpBasic
                .antMatchers("/actuator/**").hasRole("ROOT")
                .and().httpBasic();

        http.authorizeRequests()
                // /user下的请求需要user全部权限
                .antMatchers("/user/**").hasAuthority("user")
                // /admin 下的请求需要admin权限
                .antMatchers("/admin/**").hasAuthority("admin")
                // /authenticated 下的请求允许认证过的用户访问
                .antMatchers("/authenticated/**").authenticated()
                // 其余的请求允许所有用户无条件访问
                .antMatchers("/login", "/getConsumer","/my/test3").permitAll()
                .anyRequest().authenticated()
                .and()
                .csrf().disable();
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
}

  1. 自定义实现UsernamePasswordAuthenticationFilter过滤器
package com.cloud.consumer.config.security;

import com.cloud.consumer.config.security.model.User;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @author admin
 */
@Component
public class AuthorizationTokenFilter extends OncePerRequestFilter {

    private final RedisTemplate redisTemplate;

    // redis
    public AuthorizationTokenFilter(@Qualifier("redisTemplateJdk") RedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
        String authorization = request.getHeader("Authorization");

        if (StringUtils.isNotEmpty(authorization) && SecurityContextHolder.getContext().getAuthentication() == null) {
            // 从redis中取出用户信息
            Object object =  redisTemplate.opsForValue().get(authorization);
            if (object != null) {
                User user = (User) object;
                // 创建UsernamePasswordAuthenticationToken 
                UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(user, null,user.getAuthorities());
                // 添加至上下文中
                SecurityContextHolder.getContext().setAuthentication(authentication);
            }
        }
        chain.doFilter(request, response);
    }
}

  1. 创建UserDetailsServiceImpl类
package com.cloud.consumer.config.security;

import com.cloud.consumer.config.security.model.Auth;
import com.cloud.consumer.config.security.model.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

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


/**
 * @author admin
 */
@Service("userDetailsService")
public class UserDetailsServiceImpl implements UserDetailsService {

    @Override
    public User loadUserByUsername(String userName) throws UsernameNotFoundException {
        // 固定权限 实际开发中读数据库
        User user = new User();
        user.setUserName(userName);
        user.setPassword("123456");
        List<Auth> list = new ArrayList<>();
        list.add(new Auth("admin"));
        user.setList(list);
        return user;
    }
}

  1. 登录
    @PostMapping("/login")
    public AjaxResult login(@NotNull(message = "用户名不能为null") String userName, @NotNull(message = "密码不能为null") String password){
        User user = userDetailsService.loadUserByUsername(userName);
        if (StringUtils.equals(user.getPassword(),password)) {
            String token = UUID.randomUUID().toString().replaceAll("-", "");
            redisTemplate.opsForValue().set(token,user);
            AjaxResult ajaxResult = AjaxResult.successData(token);
            return ajaxResult;
        } else {
            return AjaxResult.error("密码错误");
        }
    }
  1. 测试controller
    @GetMapping("/admin/test1")
    public AjaxResult test1() {
        return AjaxResult.success("test1");
    }

    @GetMapping("/user/test2")
    public AjaxResult test2() {
        return AjaxResult.success("test2");
    }

    @GetMapping("/testPer1")
    @PreAuthorize("hasAnyAuthority('member')")
    public AjaxResult testPer1() {
        return AjaxResult.success("test3");
    }

    @GetMapping("/testPer2")
    @PreAuthorize("hasAnyAuthority('admin')")
    public AjaxResult testPer2() {
        return AjaxResult.success("test4");
    }

记录问题

1. @PreAuthorize 注解无效
@EnableGlobalMethodSecurity(prePostEnabled = true) 是否开启
@PreAuthorize 是基于aop实现,只能注解在public方法上

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值