Springboot2 + Shiro + Redis + Jwt 前后端分离整合(1)

最近看了下shiro这个框架,感觉使用还蛮方便的,话不多说直接上代码。

码云项目地址 : Springboot + shiro + redis + jwt + jpa

新建一个Springboot项目

1.pom.xml加入相关依赖

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.3.2</version>
            <exclusions>
                <exclusion>
                    <groupId>commons-beanutils</groupId>
                    <artifactId>commons-beanutils</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>3.5.0</version>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.7.0</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.61</version>
        </dependency>
        <dependency>
            <groupId>org.crazycake</groupId>
            <artifactId>shiro-redis</artifactId>
            <version>3.2.0</version>
        </dependency>
    </dependencies>

2. application.yml配置

server:
  #端口号
  port: 9002

spring:
  # 数据源设置
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/shirotest?useUnicode=true&characterEncoding=UTF-8&useOldAliasMetadataBehavior=true&serverTimezone=Asia/Shanghai
    username: root
    password: 123456

  jpa:
    hibernate:
      ddl-auto: update

  redis:
    password:
    timeout: 2000s

3. 实体类及repository

用户表

@Entity
@Data
@Table(name = "t_user")
@FieldDefaults(level = AccessLevel.PRIVATE)
public class UserEntity implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    Long id;
    String userName;
    String password;

    @Transient
    String token;
}

权限表

@Data
@Entity
@Table(name="t_role")
public class RoleEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    Long id;
    String name;
    String roleName;
}

用户权限中间表

@Entity
@Data
@Table(name="t_permission")
public class PermissionEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    Long id;
    Long userId;
    Long roleId;
}

Repository

public interface UserRepository extends JpaRepository<UserEntity,Long> {
    UserEntity findFirstByUserName(String userName);
}

public interface PermissionRepsitory extends JpaRepository<PermissionEntity,Long> {
	List<PermissionEntity> findByUserId(Long userId);
}

4. 配置类

redis配置

@Configuration
public class RedisConfig {
   

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
   
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
         /*
             设置这些是 当redis的value设置为Jackson2JsonRedisSerializer,导致shiro反序列化时出错
             直接使用JdkSerializationRedisSerializer不会出错,但是在RedisDesktopManager中,无法查看保存的数据
        */
        ObjectMapper om = new ObjectMapper();
        //在反序列化时忽略在JSON字符串中存在,而在Java中不存在的属性
        om.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        // key采用String的序列化方式
        redisTemplate.setKeySerializer(stringRedisSerializer);
        // hash的key也采用String的序列化方式
        redisTemplate.setHashKeySerializer(stringRedisSerializer);
        // value序列化方式采用jackson
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
        // hash的value序列化方式采用jackson
        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
        redisTemplate.afterPropertiesSet();

        return redisTemplate;
    }
}

我们使用的是JwtToken来代替sessionId,所以创建了AuthToken类
AuthToken

public class AuthToken implements AuthenticationToken {
   

    private String token;
    public AuthToken() {
   
    }
    public AuthToken(String token) {
   
        this.token = token;
    }
    public String getToken() {
   
        return token;
    }
    @Override
    public Object getPrincipal() {
   
        return token;
    }
    @Override
    public Object getCredentials() {
   
        return token;
    }
}    

AuthRealm 是自定义用户登陆认证、权限设置

  1. 用户认证:初登陆外其余的请求在header头中必须带token这个标志,我们根据token标志到redis判断。我这里做了单点、登出后的token无效、token验证
  2. 授权:在用户认证成功后可以给该用户授予业务权限
@Slf4j
@Component
public class AuthRealm extends AuthorizingRealm {
   

    @Autowired
    private PermissionRepsitory permissionRepsitory;

    //必须重写,不然会报错
    @Override
    public boolean supports(AuthenticationToken token) {
   
        return token instanceof AuthToken;
    }

    //授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
   
        log.debug("开始执行授权操作.......");
        System.out.println("调用了授权方法");

        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        //如果身份认证的时候没有传入User对象,这里只能取到userName
        //也就是SimpleAuthenticationInfo构造的时候第一个参数传递需要User对象
        UserEntity user = (UserEntity) principalCollection.getPrimaryPrincipal();
        Long userId = user.getId();

        List<PermissionEntity> list = permissionRepsitory.findByUserId(userId);
        if(!list.isEmpty()){
   
            list.forEach(o ->{
   
                authorizationInfo.addStringPermission(o.getRoleId().toString());
            });
        }

        return authorizationInfo;
    }

    //验证用户
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
   
        log.info("验证开始。。。");
        String token = (String) authenticationToken.getCredentials();
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值