SpringBoot JWT+SpringSecurity权限管理,使用JSON交互

SpringBoot JWT+SpringSecurity权限管理,使用JSON交互

什么是SpringSecurity

Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring IoC,DI(控制反转Inversion of Control ,DI:Dependency Injection 依赖注入)和AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能,减少了为企业系统安全控制编写大量重复代码的工作。

多的就不做介绍了,这个框架大家应该都很明白,直接步入正题吧!
此篇只写了JWT怎么和Security怎么整合在一起的流程作为笔记。想了解JWT的可去我的博客JWT 这篇有细致讲解。

一、建立数据库

首先创建对应数据库:
user 存放用户
在这里插入图片描述
role存放所有权限

在这里插入图片描述
user_role 存放用户和权限的关系
在这里插入图片描述
menu 存放动态地址
在这里插入图片描述
menu_role存放动态地址和权限之间的关系
在这里插入图片描述

二、SpringBoot配置

POM

  <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

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

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>2.3</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.20</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.6</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
            <version>2.8.6</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.54</version>
        </dependency>

application.properties

server.port=8080

#mysql
spring.datasource.url=jdbc:mysql://192.168.2.113:3306/securitytest?useUnicode=true&characterEncoding=utf8
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.type =com.alibaba.druid.pool.DruidDataSource
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
#mybatis-plus
mybatis-plus.mapper-locations=classpath:Mapper/*.xml
mybatis-plus.type-aliases-package=com.bxc.securitytest.Entity
mybatis-plus.configuration.map-underscore-to-camel-case: true

三、user实体类

@TableName(value = "user")  //表名
public class User implements UserDetails {
    private int id;
    private String username;
    private String password;
    private Boolean enabled;
    private Boolean locked;
    @TableField(exist = false)
    private List<Role> roles;

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        List<SimpleGrantedAuthority> authorities = new ArrayList<>();
        for (Role role : roles) {
            System.out.println("登录权限:"+role.getRole());
            authorities.add(new SimpleGrantedAuthority(role.getRole()));
        }
        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;
    }


    //省略getter/setter

    public Integer getId() {
        return id;
    }

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

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

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

//    public Boolean getEnabled() {
//        return enabled;
//    }

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

    public Boolean getLocked() {
        return locked;
    }

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

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

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

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", enabled=" + enabled +
                ", locked=" + locked +
                ", roles=" + roles +
                '}';
    }
}

四、数据库Mapper

@Mapper
@Component
public interface UserMapper extends BaseMapper<User> {
    @Select("select * from user where username = #{username}")
    User findUserFromUsername(String username);

    @Insert("insert into user(username,password,enabled,locked) value(#{username},#{password},#{enabled},#{locked})")
    @Options(useGeneratedKeys = true,keyProperty = "id",keyColumn = "id")
    int addUser(User user);

}
@Mapper
@Component
public interface RoleMapper extends BaseMapper<Role> {
    @Select("select a.* from role a,user_role b where a.id = b.roleid and b.userid = #{id}")
    List<Role> findRoleFromUserId(int id);

    @Select("select id from role where role = #{rolename}")
    int findRoleIdFromName(String rolename);

    @Insert("insert into user_role(userid,roleid) value(#{userid},#{roleid})")
    int insertUserRole(@Param("userid") int userid, @Param("roleid") int roleid);
}
@Mapper
@Component
public interface MenuMapper {
    List<Menu> findMenu();
}
<?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="com.bxc.securitytest.Mapper.MenuMapper">

    <resultMap id="findMenuRest" type="com.bxc.securitytest.Entity.Menu">
        <id property="id" column="id"/>
        <result property="pattern" column="pattern"/>
        <collection property="roles" ofType="com.bxc.securitytest.Entity.Role">
            <id property="id" column="rid"/>
            <result property="role" column="role"/>
            <result property="role_describe" column="role_describe"/>
        </collection>
    </resultMap>

    <select id="findMenu" resultMap="findMenuRest">
        select a.*,b.id as rid,b.role as role,b.role_describe as role_describe from menu a,role b,menu_role c where a.id = c.menu_id and b.id = c.role_id;
    </select>
</mapper>

五、Service

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

    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
        User user = userMapper.findUserFromUsername(s);
        if (user == null) {
            throw new UsernameNotFoundException("账户不存在!");
        }
        user.setRoles(roleMapper.findRoleFromUserId(user.getId()));
        System.out.println(user);
        return user;
    }
}
@Service
@Slf4j
public class RoleService {
    @Autowired
    UserMapper userMapper;
    @Autowired
    RoleMapper roleMapper;
    @Autowired
    MenuMapper menuMapper;

    public boolean AddUser(String username, String password,String role) {
        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(10);
        String encodePasswod = encoder.encode(password);
        return saveToDb(username, encodePasswod,role);
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public boolean saveToDb(String username, String encodePasswod,String role) {
        User user = new User();
        user.setUsername(username);
        user.setPassword(encodePasswod);
        user.setEnabled(true);
        user.setLocked(false);
        log.info(user.toString());
        System.out.println(user.toString());
        int count = userMapper.addUser(user);
        if (count > 0){
            System.out.println(user.toString());
            int roleid = roleMapper.findRoleIdFromName(role);
            System.out.println(roleid);
            int c = roleMapper.insertUserRole(user.getId(),roleid);
            if (c > 0){
                return true;
            }
        }
        return false;
    }


    public List<Menu> findMeun(){
        return menuMapper.findMenu();
    }
}

六、重写登录过滤器

public class LoginAuthenticationFilter extends UsernamePasswordAuthenticationFilter {

    @SneakyThrows
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        response.setContentType("text/json;charset=utf-8");
        if(request.getContentType().equals(MediaType.APPLICATION_JSON_UTF8_VALUE) ||request.getContentType().equals(MediaType.APPLICATION_JSON_VALUE)){
            BufferedReader br = null;
            try {
                br = new BufferedReader(new InputStreamReader(request.getInputStream(),"utf-8"));
                String line = null;
                StringBuilder sb = new StringBuilder();
                while ((line = br.readLine()) != null) {
                    sb.append(line);
                }
                JSONObject json= JSONObject.parseObject(sb.toString());
                System.out.println(json.toString());

                String username = json.getString("username");
                String password = json.getString("password");

                UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
                this.setDetails(request, authRequest);
                return this.getAuthenticationManager().authenticate(authRequest);
            } catch (IOException e) {
                e.printStackTrace();
                response.getWriter().write(JSON.toJSONString(ResSuccessTemplate.builder().code(400).message("参数错误!").build()));
            }
        } else {
            return super.attemptAuthentication(request, response);
        }
        response.getWriter().write(JSON.toJSONString(ResSuccessTemplate.builder().code(400).message("参数错误!").build()));
        return null;
    }

}

七、登录成功Handler

@Component
public class AuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
    @Autowired
    JWTUtil jwtUtil;

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        User user = (User)authentication.getPrincipal();

        String username = user.getUsername();
        List<GrantedAuthority> authorities = (List<GrantedAuthority>) user.getAuthorities();
        List<String> list = new ArrayList<>();
        String role = "";
        for(GrantedAuthority g : authorities){
            list.add(g.getAuthority());
            role = g.getAuthority();
        }

        String  token = jwtUtil.CreateToken(user.getId()+"",username,role);

        response.setContentType("text/json;charset=utf-8");
        Map<String,Object> map = new HashMap<>();
        map.put("username",username);
        map.put("role",role);
        map.put("token",token);
        response.getWriter().write(JSON.toJSONString(ResSuccessTemplate.builder().data(map).build()));
    }
}

八、登录失败Handler

@Component
public class AuthenticationFailHandler extends SimpleUrlAuthenticationFailureHandler {

    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException {

        response.setContentType("text/json;charset=utf-8");

        if (e instanceof UsernameNotFoundException || e instanceof BadCredentialsException) {

            response.getWriter().write(JSON.toJSONString(ResFailTemplate.builder().code(400).message("用户名或密码错误!").build()));

        } else if (e instanceof DisabledException) {

            response.getWriter().write(JSON.toJSONString(ResFailTemplate.builder().code(400).message("账户被禁用,请联系管理员!").build()));

        } else {

            response.getWriter().write(JSON.toJSONString(ResFailTemplate.builder().code(400).message("用户名或密码错误!").build()));

        }
    }
}

九、403无权限Handler

@Component
public class AuthenticationAccessDeniedHandler implements AccessDeniedHandler {

    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException)
            throws IOException, ServletException {
        response.setContentType("text/json;charset=utf-8");
        response.getWriter().write(JSON.toJSONString(ResFailTemplate.builder().code(403).message("权限不足!").build()));
    }
}

十、拦截Token 过滤器

@Slf4j
public class JWTAuthenticationFilter extends BasicAuthenticationFilter {

    @Autowired
    JWTUtil jwtUtil;

    private Integer tokenExpireTime;

    public JWTAuthenticationFilter(AuthenticationManager authenticationManager, Integer tokenExpireTime) {
        super(authenticationManager);
        this.tokenExpireTime = tokenExpireTime;
    }

    public JWTAuthenticationFilter(AuthenticationManager authenticationManager, AuthenticationEntryPoint authenticationEntryPoint) {
        super(authenticationManager, authenticationEntryPoint);
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {

        String token = request.getHeader("token");

        if (jwtUtil == null){
            jwtUtil = new JWTUtil();
        }

        boolean isold = jwtUtil.VerityToken(token);

        response.setContentType("text/json;charset=utf-8");

        if (!isold) {
            response.getWriter().write(JSON.toJSONString(ResFailTemplate.builder().code(401).message("登录失效!").build()));
            return;
        }

        String username = jwtUtil.getUserName(token);

        if (StringUtils.isEmpty(username)){
            response.getWriter().write(JSON.toJSONString(ResFailTemplate.builder().code(401).message("登录失效!").build()));
            return;
        }

        String role = jwtUtil.getUserRoles(token);
        List<GrantedAuthority> authorities = new ArrayList<>();

        if(StringUtils.isEmpty(role)){
            response.getWriter().write(JSON.toJSONString(ResFailTemplate.builder().code(403).message("权限不足!").build()));
            return;
        }
        authorities.add(new SimpleGrantedAuthority(role));

        User principal = new User();
        principal.setUsername(username);
        try {
            UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(principal, null, authorities);
            SecurityContextHolder.getContext().setAuthentication(authentication);
            String userid = jwtUtil.getUserid(token);

            request.setAttribute("userid",userid);
            request.setAttribute("username",username);
            request.setAttribute("role",role);
            System.out.println("过滤权限:"+role);
            chain.doFilter(request, response);
        }catch (Exception e){
            e.printStackTrace();
            response.getWriter().write(JSON.toJSONString(ResFailTemplate.builder().code(401).message("登录失效!").build()));
        }
    }
}

十一、配置SecurityConfig

@EnableGlobalMethodSecurity(prePostEnabled = true)

@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    UserService userService;

    @Autowired
    RoleService roleService;

    @Autowired
    AuthenticationSuccessHandler successHandler;

    @Autowired
    AuthenticationFailHandler failHandler;

    @Autowired
    AuthenticationAccessDeniedHandler deniedHandler;


    @Bean
    RoleHierarchy roleHierarchy() {
        RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl();
        String hierarchy = "ROLE_admin > ROLE_user > ROLE_common";
        roleHierarchy.setHierarchy(hierarchy);
        return roleHierarchy;
    }


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

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userService);
    }


    @Bean
    LoginAuthenticationFilter customAuthenticationFilter() throws Exception {
        LoginAuthenticationFilter filter = new LoginAuthenticationFilter();
        filter.setAuthenticationSuccessHandler(successHandler);
        filter.setAuthenticationFailureHandler(failHandler);
        filter.setFilterProcessesUrl("/login");
        //这句很关键,重用WebSecurityConfigurerAdapter配置的AuthenticationManager,不然要自己组装AuthenticationManager
        filter.setAuthenticationManager(authenticationManagerBean());
        return filter;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry authorizeRequests = http
                .authorizeRequests();

        List<Menu> menus = roleService.findMeun();
        for (Menu m:menus){
            System.out.println(m);
            String role = "";
            for (int i=0;i<m.getRoles().size();i++){
                if (i == m.getRoles().size()-1){
                    role+= "hasRole('"+m.getRoles().get(i).getRole()+"')";
                }else {
                    role += "hasRole('"+m.getRoles().get(i).getRole()+"') and ";
                }
            }
            authorizeRequests.antMatchers(m.getPattern()).access(role);
        }

        authorizeRequests
                .antMatchers("/**")
                .permitAll()

                .anyRequest()
                .authenticated()
                .and()
                .formLogin()
                .loginProcessingUrl("/login")
                .permitAll()
                .and()
                .csrf()
                .disable()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .addFilter(new JWTAuthenticationFilter(authenticationManager(), 7))
                .exceptionHandling()
                .accessDeniedHandler(deniedHandler);

        http.addFilterAt(customAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);

    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        // web.ignoring是直接绕开spring security的所有filter,直接跳过验证
        web.ignoring()
                .antMatchers(HttpMethod.OPTIONS, "/AddUser");
        ;
    }
}

十二、测试

@RestController
public class TestController {
    @Autowired
    UserService userService;
    @Autowired
    RoleService roleService;

    @GetMapping("/Admin/AdminTest")
    public String AdminTest(){
        return "Admin - Success";
    }

    @GetMapping("/User/UserTest")
    public String UserTest(){
        return "User - Success";
    }

    @GetMapping("/Common/CommonTest")
    public String CommonTest(HttpServletRequest request){
        String userid = (String) request.getAttribute("userid");
        String username = (String) request.getAttribute("username");
        String role = (String) request.getAttribute("role");

        return "Common - Success"+"userid="+userid+", username="+username+",role="+role;
    }
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小毕超

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值